aiobungie

A statically typed, asynchronous API wrapper for building clients for Bungie's API in Python.

Getting Started

This is the basic client you probably want you start with.

import aiobungie

client = aiobungie.Client('YOUR_API_KEY', client_secret='KEY', client_id=0)

async def main() -> None:
    async with client.rest:
        # Search for Destiny 2 memberships.
        users = await client.search_users('Crit')

        # Iterate over the users and take the first 5 results.
        for user in users.take(5):
            # Print the user name and their code.
            print(f'{user.name} {user.code}')

# aiobungie provides an internal function to run async functions.
# It's equivalent to asyncio.run()
client.run(main()) # or asyncio.run(main())
RESTClient

aiobungie provides a second way to use Bungie's API,

a single RESTClient allows you to make requests and return JSON objects immediately.

This bypasses the need to deserialize and create objects. It also exposes all OAuth2 and manifest methods. This can be faster for REST apis.

This is considered the core client since aiobungie.Client is built on top of it. Using the aiobungie.rest property allows direct access to the raw REST client instance.

import aiobungie
import asyncio

client = aiobungie.RESTClient("TOKEN")

async def main() -> None:
    async with client as rest:
        payload = await rest.fetch_player('Fate怒', 4275)

        for membership in payload:
            print(membership['membershipId'])

asyncio.run(main())
RESTPool

A REST client pool allows you to acquire multiple RESTClient that share the same state.

This is useful when you want to spawn an instance for each client which shared the same state.

import aiobungie
import asyncio

pool = aiobungie.RESTPool("token")

async def set() -> None:
    # Set your ID to access it from other places.
    pool.metadata['my_id'] = 4401
    async with pool.acquire() as instance:
        ...

async def fetch() -> None:
    my_id: int = pool.metadata['my_id']
    async with pool.acquire() as instance: # A different client instance.
        my_user = instance.fetch_bungie_user(my_id)

await asyncio.gather(set(), fetch())

When should you use which client?

  • Use Client when you want to build a Chat Bot, Discord Bot, access data as Python classes.
  • Use RESTClient when you want one TCP session for all clients, access data as JSON payloads.
  • Use RESTPool when you're serving a large amount of connections and want to spawn a session for each, access data as JSON payloads. Note that setting up multiple TCP connections can be expensive.
  1# MIT License
  2#
  3# Copyright (c) 2020 - Present nxtlo
  4#
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11#
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14#
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22
 23"""A statically typed, asynchronous API wrapper for building clients for Bungie's API in Python.
 24
 25Getting Started
 26---------------
 27
 28This is the basic client you probably want you start with.
 29
 30```py
 31import aiobungie
 32
 33client = aiobungie.Client('YOUR_API_KEY', client_secret='KEY', client_id=0)
 34
 35async def main() -> None:
 36    async with client.rest:
 37        # Search for Destiny 2 memberships.
 38        users = await client.search_users('Crit')
 39
 40        # Iterate over the users and take the first 5 results.
 41        for user in users.take(5):
 42            # Print the user name and their code.
 43            print(f'{user.name} {user.code}')
 44
 45# aiobungie provides an internal function to run async functions.
 46# It's equivalent to asyncio.run()
 47client.run(main()) # or asyncio.run(main())
 48```
 49
 50RESTClient
 51----------
 52
 53aiobungie provides a second way to use Bungie's API,
 54
 55a single `RESTClient` allows you to make requests and return JSON objects immediately.
 56
 57This bypasses the need to deserialize and create objects. It also exposes all `OAuth2` and `manifest` methods.
 58This can be faster for `REST` apis.
 59
 60This is considered the core client since `aiobungie.Client` is built on top of it.
 61Using the `.rest` property allows direct access to the raw REST client instance.
 62
 63
 64```py
 65import aiobungie
 66import asyncio
 67
 68client = aiobungie.RESTClient("TOKEN")
 69
 70async def main() -> None:
 71    async with client as rest:
 72        payload = await rest.fetch_player('Fate怒', 4275)
 73
 74        for membership in payload:
 75            print(membership['membershipId'])
 76
 77asyncio.run(main())
 78```
 79
 80RESTPool
 81--------
 82
 83A REST client pool allows you to acquire multiple `RESTClient` that share the same state.
 84
 85This is useful when you want to spawn an instance for each client which shared the same state.
 86
 87```py
 88import aiobungie
 89import asyncio
 90
 91pool = aiobungie.RESTPool("token")
 92
 93async def set() -> None:
 94    # Set your ID to access it from other places.
 95    pool.metadata['my_id'] = 4401
 96    async with pool.acquire() as instance:
 97        ...
 98
 99async def fetch() -> None:
100    my_id: int = pool.metadata['my_id']
101    async with pool.acquire() as instance: # A different client instance.
102        my_user = instance.fetch_bungie_user(my_id)
103
104await asyncio.gather(set(), fetch())
105```
106
107## When should you use which client?
108* Use `Client` when you want to build a Chat Bot, Discord Bot, access data as Python classes.
109* Use `RESTClient` when you want one TCP session for all clients, access data as JSON payloads.
110* Use `RESTPool` when you're serving a large amount of connections and want to spawn a session for each,
111access data as JSON payloads.
112Note that setting up multiple TCP connections can be expensive.
113"""
114
115
116from __future__ import annotations
117
118from aiobungie import builders
119from aiobungie import crates
120from aiobungie import interfaces
121from aiobungie import traits
122from aiobungie import typedefs
123from aiobungie import url
124from aiobungie.client import Client
125from aiobungie.error import *
126from aiobungie.internal import iterators
127from aiobungie.internal.assets import Image
128from aiobungie.internal.enums import *
129from aiobungie.internal.factory import Factory
130from aiobungie.internal.iterators import *
131from aiobungie.rest import *
132from aiobungie.undefined import UNDEFINED
133from aiobungie.undefined import UndefinedOr
134from aiobungie.undefined import UndefinedType
135
136from .metadata import __about__
137from .metadata import __author__
138from .metadata import __docs__
139from .metadata import __email__
140from .metadata import __license__
141from .metadata import __url__
142from .metadata import __version__
143
144# Alias for crate for backwards compatibility.
145crate = crates
146
147# Activity enums
148from .crates.activity import Difficulty
149
150# Components enums
151from .crates.components import ComponentFields
152from .crates.components import ComponentPrivacy
153
154# Entity enums
155from .crates.entity import GatingScope
156from .crates.entity import ObjectiveUIStyle
157from .crates.entity import ValueUIStyle
158
159# Fireteam enums.
160from .crates.fireteams import FireteamActivity
161from .crates.fireteams import FireteamDate
162from .crates.fireteams import FireteamLanguage
163from .crates.fireteams import FireteamPlatform
164
165# Records enums
166from .crates.records import RecordState
167
168__all__ = [mod for mod in dir() if not mod.startswith("_")]  # type: ignore
@attrs.define(auto_exc=True)
class AiobungieError(builtins.RuntimeError):
73@attrs.define(auto_exc=True)
74class AiobungieError(RuntimeError):
75    """Base class that all other exceptions inherit from."""

Base class that all other exceptions inherit from.

AiobungieError()
2def __init__(self, ):
3    BaseException.__init__(self, )

Method generated by attrs for class AiobungieError.

Inherited Members
builtins.BaseException
with_traceback
args
@typing.final
class AmmoType(builtins.int, aiobungie.Enum):
635@typing.final
636class AmmoType(int, Enum):
637    """AN enum for Detyiny 2 ammo types."""
638
639    NONE = 0
640    PRIMARY = 1
641    SPECIAL = 2
642    HEAVY = 3

AN enum for Detyiny 2 ammo types.

NONE = <AmmoType.NONE: 0>
PRIMARY = <AmmoType.PRIMARY: 1>
SPECIAL = <AmmoType.SPECIAL: 2>
HEAVY = <AmmoType.HEAVY: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class BadRequest(aiobungie.HTTPError):
163@attrs.define(auto_exc=True)
164class BadRequest(HTTPError):
165    """An exception raised when requesting a resource with the provided data is wrong."""
166
167    url: typing.Optional[typedefs.StrOrURL]
168    """The URL/endpoint caused this error."""
169
170    body: typing.Any
171    """The response body."""
172
173    headers: multidict.CIMultiDictProxy[str]
174    """The response headers."""
175
176    http_status: http.HTTPStatus = attrs.field(
177        default=http.HTTPStatus.BAD_REQUEST, init=False
178    )

An exception raised when requesting a resource with the provided data is wrong.

BadRequest( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str])
2def __init__(self, message, url, body, headers):
3    self.message = message
4    self.url = url
5    self.body = body
6    self.headers = headers
7    self.http_status = attr_dict['http_status'].default
8    BaseException.__init__(self, self.message,self.url,self.body,self.headers)

Method generated by attrs for class BadRequest.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

http_status: http.HTTPStatus

The response status.

Inherited Members
HTTPError
message
builtins.BaseException
with_traceback
args
@typing.final
class ClanMemberType(builtins.int, aiobungie.Enum):
690@typing.final
691class ClanMemberType(int, Enum):
692    """An enum for bungie clan member types."""
693
694    NONE = 0
695    BEGINNER = 1
696    MEMBER = 2
697    ADMIN = 3
698    ACTING_FOUNDER = 4
699    FOUNDER = 5

An enum for bungie clan member types.

NONE = <ClanMemberType.NONE: 0>
BEGINNER = <ClanMemberType.BEGINNER: 1>
MEMBER = <ClanMemberType.MEMBER: 2>
ADMIN = <ClanMemberType.ADMIN: 3>
ACTING_FOUNDER = <ClanMemberType.ACTING_FOUNDER: 4>
FOUNDER = <ClanMemberType.FOUNDER: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Class(builtins.int, aiobungie.Enum):
466@typing.final
467class Class(int, Enum):
468    """An Enum for Destiny character classes."""
469
470    TITAN = 0
471    HUNTER = 1
472    WARLOCK = 2
473    UNKNOWN = 3

An Enum for Destiny character classes.

TITAN = <Class.TITAN: 0>
HUNTER = <Class.HUNTER: 1>
WARLOCK = <Class.WARLOCK: 2>
UNKNOWN = <Class.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Client(aiobungie.traits.ClientApp):
  60class Client(traits.ClientApp):
  61    """Standard Bungie API client application.
  62
  63    This client deserialize the REST JSON responses using `aiobungie.internal.factory.Factory`
  64    and returns `aiobungie.crates` Python object implementations of the responses.
  65
  66    A `aiobungie.RESTClient` REST client can also be used alone for low-level concepts.
  67
  68    Example
  69    -------
  70    ```py
  71    import aiobungie
  72
  73    client = aiobungie.Client('...')
  74
  75    async def main():
  76        async with client.rest:
  77            user = await client.fetch_current_user_memberships('...')
  78            print(user)
  79    ```
  80
  81    Parameters
  82    -----------
  83    token: `str`
  84        Your Bungie's API key or Token from the developer's portal.
  85
  86    Other Parameters
  87    ----------------
  88    max_retries : `int`
  89        The max retries number to retry if the request hit a `5xx` status code.
  90    client_secret : `str | None`
  91        An optional application client secret,
  92        This is only needed if you're fetching OAuth2 tokens with this client.
  93    client_id : `int | None`
  94        An optional application client id,
  95        This is only needed if you're fetching OAuth2 tokens with this client.
  96    """
  97
  98    __slots__ = ("_rest", "_factory")
  99
 100    def __init__(
 101        self,
 102        token: str,
 103        /,
 104        *,
 105        client_secret: typing.Optional[str] = None,
 106        client_id: typing.Optional[int] = None,
 107        dumps: typedefs.Dumps = helpers.dumps,
 108        loads: typedefs.Loads = helpers.loads,
 109        max_retries: int = 4,
 110    ) -> None:
 111        self._rest = rest_.RESTClient(
 112            token,
 113            dumps=dumps,
 114            loads=loads,
 115            client_secret=client_secret,
 116            client_id=client_id,
 117            max_retries=max_retries,
 118        )
 119
 120        self._factory = factory_.Factory(self)
 121
 122    @property
 123    def factory(self) -> factory_.Factory:
 124        return self._factory
 125
 126    @property
 127    def rest(self) -> interfaces.RESTInterface:
 128        return self._rest
 129
 130    @property
 131    def request(self) -> Client:
 132        return self
 133
 134    @property
 135    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 136        return self._rest.metadata
 137
 138    def run(
 139        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
 140    ) -> None:
 141        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
 142        try:
 143            if not loop.is_running():
 144                loop.set_debug(debug)
 145                loop.run_until_complete(future)
 146
 147        except Exception as exc:
 148            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
 149
 150        except KeyboardInterrupt:
 151            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
 152            return
 153
 154    # * User methods.
 155
 156    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
 157        """Fetch and return a user object of the bungie net user associated with account.
 158
 159        .. warning::
 160            This method requires OAuth2 scope and a Bearer access token.
 161
 162        Parameters
 163        ----------
 164        access_token : `str`
 165            A valid Bearer access token for the authorization.
 166
 167        Returns
 168        -------
 169        `aiobungie.crates.user.User`
 170            A user object includes the Destiny memberships and Bungie.net user.
 171        """
 172        resp = await self.rest.fetch_current_user_memberships(access_token)
 173
 174        return self.factory.deserialize_user(resp)
 175
 176    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
 177        """Fetch a Bungie user by their BungieNet id.
 178
 179        .. note::
 180            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
 181            for other memberships.
 182
 183        Parameters
 184        ----------
 185        id: `int`
 186            The user id.
 187
 188        Returns
 189        -------
 190        `aiobungie.crates.user.BungieUser`
 191            A Bungie user.
 192
 193        Raises
 194        ------
 195        `aiobungie.error.NotFound`
 196            The user was not found.
 197        """
 198        payload = await self.rest.fetch_bungie_user(id)
 199
 200        return self.factory.deserialize_bungie_user(payload)
 201
 202    async def search_users(
 203        self, name: str, /
 204    ) -> iterators.Iterator[user.SearchableDestinyUser]:
 205        """Search for players and return all players that matches the same name.
 206
 207        Parameters
 208        ----------
 209        name : `buildins.str`
 210            The user name.
 211
 212        Returns
 213        -------
 214        `aiobungie.iterators.Iterator[aiobungie.crates.DestinyMembership]`
 215            A sequence of destiny memberships.
 216        """
 217        payload = await self.rest.search_users(name)
 218
 219        return iterators.Iterator(
 220            [
 221                self.factory.deserialize_searched_user(user)
 222                for user in payload["searchResults"]
 223            ]
 224        )
 225
 226    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
 227        """Fetch all available user themes.
 228
 229        Returns
 230        -------
 231        `collections.Sequence[aiobungie.crates.user.UserThemes]`
 232            A sequence of user themes.
 233        """
 234        data = await self.rest.fetch_user_themes()
 235
 236        return self.factory.deserialize_user_themes(data)
 237
 238    async def fetch_hard_types(
 239        self,
 240        credential: int,
 241        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
 242        /,
 243    ) -> user.HardLinkedMembership:
 244        """Gets any hard linked membership given a credential.
 245        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
 246        Cross Save aware.
 247
 248        Parameters
 249        ----------
 250        credential: `int`
 251            A valid SteamID64
 252        type: `aiobungie.CredentialType`
 253            The credential type. This must not be changed
 254            Since its only credential that works "currently"
 255
 256        Returns
 257        -------
 258        `aiobungie.crates.user.HardLinkedMembership`
 259            Information about the hard linked data.
 260        """
 261
 262        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
 263
 264        return user.HardLinkedMembership(
 265            id=int(payload["membershipId"]),
 266            type=enums.MembershipType(payload["membershipType"]),
 267            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
 268        )
 269
 270    async def fetch_membership_from_id(
 271        self,
 272        id: int,
 273        /,
 274        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 275    ) -> user.User:
 276        """Fetch Bungie user's memberships from their id.
 277
 278        Notes
 279        -----
 280        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
 281        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
 282        see `aiobungie.crates.user.DestinyMembership` for more details.
 283        * If you only want the bungie user. Consider using `Client.fetch_user` method.
 284
 285        Parameters
 286        ----------
 287        id : `int`
 288            The user's id.
 289        type : `aiobungie.MembershipType`
 290            The user's membership type.
 291
 292        Returns
 293        -------
 294        `aiobungie.crates.User`
 295            A Bungie user with their membership types.
 296
 297        Raises
 298        ------
 299        aiobungie.NotFound
 300            The requested user was not found.
 301        """
 302        payload = await self.rest.fetch_membership_from_id(id, type)
 303
 304        return self.factory.deserialize_user(payload)
 305
 306    async def fetch_user_credentials(
 307        self, access_token: str, membership_id: int, /
 308    ) -> collections.Sequence[user.UserCredentials]:
 309        """Fetch an array of credential types attached to the requested account.
 310
 311        .. note::
 312            This method require OAuth2 Bearer access token.
 313
 314        Parameters
 315        ----------
 316        access_token : `str`
 317            The bearer access token associated with the bungie account.
 318        membership_id : `int`
 319            The id of the membership to return.
 320
 321        Returns
 322        -------
 323        `collections.Sequence[aiobungie.crates.UserCredentials]`
 324            A sequence of the attached user credentials.
 325
 326        Raises
 327        ------
 328        `aiobungie.Unauthorized`
 329            The access token was wrong or no access token passed.
 330        """
 331        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
 332
 333        return self.factory.deserialize_user_credentials(resp)
 334
 335    # * Destiny 2.
 336
 337    async def fetch_profile(
 338        self,
 339        member_id: int,
 340        type: typedefs.IntAnd[enums.MembershipType],
 341        components: list[enums.ComponentType],
 342        auth: typing.Optional[str] = None,
 343    ) -> components.Component:
 344        """
 345        Fetch a bungie profile passing components to the request.
 346
 347        Parameters
 348        ----------
 349        member_id: `int`
 350            The member's id.
 351        type: `aiobungie.MembershipType`
 352            A valid membership type.
 353        components : `list[aiobungie.ComponentType]`
 354            List of profile components to collect and return.
 355
 356        Other Parameters
 357        ----------------
 358        auth : `typing.Optional[str]`
 359            A Bearer access_token to make the request with.
 360            This is optional and limited to components that only requires an Authorization token.
 361
 362        Returns
 363        --------
 364        `aiobungie.crates.Component`
 365            A Destiny 2 player profile with its components.
 366            Only passed components will be available if they exists. Otherwise they will be `None`
 367
 368        Raises
 369        ------
 370        `aiobungie.MembershipTypeError`
 371            The provided membership type was invalid.
 372        """
 373        data = await self.rest.fetch_profile(member_id, type, components, auth)
 374        return self.factory.deserialize_components(data)
 375
 376    async def fetch_linked_profiles(
 377        self,
 378        member_id: int,
 379        member_type: typedefs.IntAnd[enums.MembershipType],
 380        /,
 381        *,
 382        all: bool = False,
 383    ) -> profile.LinkedProfile:
 384        """Returns a summary information about all profiles linked to the requested member.
 385
 386        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
 387
 388        .. note::
 389            It will only return linked accounts whose linkages you are allowed to view.
 390
 391        Parameters
 392        ----------
 393        member_id : `int`
 394            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
 395        member_type : `aiobungie.MembershipType`
 396            The type for the membership whose linked Destiny account you want to return.
 397
 398        Other Parameters
 399        ----------------
 400        all : `bool`
 401            If provided and set to `True`, All memberships regardless
 402            of whether they're obscured by overrides will be returned,
 403
 404            If provided and set to `False`, Only available memberships will be returned.
 405            The default for this is `False`.
 406
 407        Returns
 408        -------
 409        `aiobungie.crates.profile.LinkedProfile`
 410            A linked profile object.
 411        """
 412        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
 413
 414        return self.factory.deserialize_linked_profiles(resp)
 415
 416    async def fetch_player(
 417        self,
 418        name: str,
 419        code: int,
 420        /,
 421        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 422    ) -> collections.Sequence[user.DestinyMembership]:
 423        """Fetch a Destiny 2 player's memberships.
 424
 425        Parameters
 426        -----------
 427        name: `str`
 428            The unique Bungie player name.
 429        code : `int`
 430            The unique Bungie display name code.
 431        type: `aiobungie.internal.enums.MembershipType`
 432            The player's membership type, e,g. XBOX, STEAM, PSN
 433
 434        Returns
 435        --------
 436        `collections.Sequence[aiobungie.crates.DestinyMembership]`
 437            A sequence of the found Destiny 2 player memberships.
 438            An empty sequence will be returned if no one found.
 439
 440        Raises
 441        ------
 442        `aiobungie.MembershipTypeError`
 443            The provided membership type was invalid.
 444        """
 445        resp = await self.rest.fetch_player(name, code, type)
 446
 447        return self.factory.deserialize_destiny_memberships(resp)
 448
 449    async def fetch_character(
 450        self,
 451        member_id: int,
 452        membership_type: typedefs.IntAnd[enums.MembershipType],
 453        character_id: int,
 454        components: list[enums.ComponentType],
 455        auth: typing.Optional[str] = None,
 456    ) -> components.CharacterComponent:
 457        """Fetch a Destiny 2 character.
 458
 459        Parameters
 460        ----------
 461        member_id: `int`
 462            A valid bungie member id.
 463        character_id: `int`
 464            The Destiny character id to retrieve.
 465        membership_type: `aiobungie.internal.enums.MembershipType`
 466            The member's membership type.
 467        components: `list[aiobungie.ComponentType]`
 468            Multiple arguments of character components to collect and return.
 469
 470        Other Parameters
 471        ----------------
 472        auth : `typing.Optional[str]`
 473            A Bearer access_token to make the request with.
 474            This is optional and limited to components that only requires an Authorization token.
 475
 476        Returns
 477        -------
 478        `aiobungie.crates.CharacterComponent`
 479            A Bungie character component.
 480
 481        `aiobungie.MembershipTypeError`
 482            The provided membership type was invalid.
 483        """
 484        resp = await self.rest.fetch_character(
 485            member_id, membership_type, character_id, components, auth
 486        )
 487
 488        return self.factory.deserialize_character_component(resp)
 489
 490    async def fetch_unique_weapon_history(
 491        self,
 492        membership_id: int,
 493        character_id: int,
 494        membership_type: typedefs.IntAnd[enums.MembershipType],
 495    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
 496        """Fetch details about unique weapon usage for a character. Includes all exotics.
 497
 498        Parameters
 499        ----------
 500        membership_id : `int`
 501            The Destiny user membership id.
 502        character_id : `int`
 503            The character id to retrieve.
 504        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 505            The Destiny user's membership type.
 506
 507        Returns
 508        -------
 509        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
 510            A sequence of the weapon's extended values.
 511        """
 512        resp = await self._rest.fetch_unique_weapon_history(
 513            membership_id, character_id, membership_type
 514        )
 515
 516        return [
 517            self._factory.deserialize_extended_weapon_values(weapon)
 518            for weapon in resp["weapons"]
 519        ]
 520
 521    # * Destiny 2 Activities.
 522
 523    async def fetch_activities(
 524        self,
 525        member_id: int,
 526        character_id: int,
 527        mode: typedefs.IntAnd[enums.GameMode],
 528        *,
 529        membership_type: typedefs.IntAnd[
 530            enums.MembershipType
 531        ] = enums.MembershipType.ALL,
 532        page: int = 0,
 533        limit: int = 250,
 534    ) -> iterators.Iterator[activity.Activity]:
 535        """Fetch a Destiny 2 activity for the specified character id.
 536
 537        Parameters
 538        ----------
 539        member_id: `int`
 540            The user id that starts with `4611`.
 541        character_id: `int`
 542            The id of the character to retrieve the activities for.
 543        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
 544            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
 545
 546        Other Parameters
 547        ----------------
 548        membership_type: `aiobungie.internal.enums.MembershipType`
 549            The Member ship type, if nothing was passed than it will return all.
 550        page: int
 551            The page number. Default is `0`
 552        limit: int
 553            Limit the returned result. Default is `250`.
 554
 555        Returns
 556        -------
 557        `aiobungie.iterators.Iterator[aiobungie.crates.Activity]`
 558            An iterator of the player's activities.
 559
 560        Raises
 561        ------
 562        `aiobungie.MembershipTypeError`
 563            The provided membership type was invalid.
 564        """
 565        resp = await self.rest.fetch_activities(
 566            member_id,
 567            character_id,
 568            mode,
 569            membership_type=membership_type,
 570            page=page,
 571            limit=limit,
 572        )
 573
 574        return self.factory.deserialize_activities(resp)
 575
 576    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
 577        """Fetch a post activity details.
 578
 579        Parameters
 580        ----------
 581        instance_id: `int`
 582            The activity instance id.
 583
 584        Returns
 585        -------
 586        `aiobungie.crates.PostActivity`
 587           A post activity object.
 588        """
 589        resp = await self.rest.fetch_post_activity(instance_id)
 590
 591        return self.factory.deserialize_post_activity(resp)
 592
 593    async def fetch_aggregated_activity_stats(
 594        self,
 595        character_id: int,
 596        membership_id: int,
 597        membership_type: typedefs.IntAnd[enums.MembershipType],
 598    ) -> iterators.Iterator[activity.AggregatedActivity]:
 599        """Fetch aggregated activity stats for a character.
 600
 601        Parameters
 602        ----------
 603        character_id: `int`
 604            The id of the character to retrieve the activities for.
 605        membership_id: `int`
 606            The id of the user that started with `4611`.
 607        membership_type: `aiobungie.internal.enums.MembershipType`
 608            The Member ship type.
 609
 610        Returns
 611        -------
 612        `aiobungie.iterators.Iterator[aiobungie.crates.AggregatedActivity]`
 613            An iterator of the player's activities.
 614
 615        Raises
 616        ------
 617        `aiobungie.MembershipTypeError`
 618            The provided membership type was invalid.
 619        """
 620        resp = await self.rest.fetch_aggregated_activity_stats(
 621            character_id, membership_id, membership_type
 622        )
 623
 624        return self.factory.deserialize_aggregated_activities(resp)
 625
 626    # * Destiny 2 Clans or GroupsV2.
 627
 628    async def fetch_clan_from_id(
 629        self,
 630        id: int,
 631        /,
 632        access_token: typing.Optional[str] = None,
 633    ) -> clans.Clan:
 634        """Fetch a Bungie Clan by its id.
 635
 636        Parameters
 637        -----------
 638        id: `int`
 639            The clan id.
 640
 641        Returns
 642        --------
 643        `aiobungie.crates.Clan`
 644            An Bungie clan.
 645
 646        Raises
 647        ------
 648        `aiobungie.NotFound`
 649            The clan was not found.
 650        """
 651        resp = await self.rest.fetch_clan_from_id(id, access_token)
 652
 653        return self.factory.deserialize_clan(resp)
 654
 655    async def fetch_clan(
 656        self,
 657        name: str,
 658        /,
 659        access_token: typing.Optional[str] = None,
 660        *,
 661        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 662    ) -> clans.Clan:
 663        """Fetch a Clan by its name.
 664        This method will return the first clan found with given name.
 665
 666        Parameters
 667        ----------
 668        name: `str`
 669            The clan name
 670
 671        Other Parameters
 672        ----------------
 673        access_token : `typing.Optional[str]`
 674            An optional access token to make the request with.
 675
 676            If the token was bound to a member of the clan,
 677            This field `aiobungie.crates.Clan.current_user_membership` will be available
 678            and will return the membership of the user who made this request.
 679        type : `aiobungie.GroupType`
 680            The group type, Default is aiobungie.GroupType.CLAN.
 681
 682        Returns
 683        -------
 684        `aiobungie.crates.Clan`
 685            A Bungie clan.
 686
 687        Raises
 688        ------
 689        `aiobungie.NotFound`
 690            The clan was not found.
 691        """
 692        resp = await self.rest.fetch_clan(name, access_token, type=type)
 693
 694        return self.factory.deserialize_clan(resp)
 695
 696    async def fetch_clan_conversations(
 697        self, clan_id: int, /
 698    ) -> collections.Sequence[clans.ClanConversation]:
 699        """Fetch the conversations/chat channels of the given clan id.
 700
 701        Parameters
 702        ----------
 703        clan_id : `int`
 704            The clan id.
 705
 706        Returns
 707        `collections.Sequence[aiobungie.crates.ClanConversation]`
 708            A sequence of the clan chat channels.
 709        """
 710        resp = await self.rest.fetch_clan_conversations(clan_id)
 711
 712        return self.factory.deserialize_clan_conversations(resp)
 713
 714    async def fetch_clan_admins(
 715        self, clan_id: int, /
 716    ) -> iterators.Iterator[clans.ClanMember]:
 717        """Fetch the clan founder and admins.
 718
 719        Parameters
 720        ----------
 721        clan_id : `int`
 722            The clan id.
 723
 724        Returns
 725        -------
 726        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
 727            An iterator over the found clan admins and founder.
 728
 729        Raises
 730        ------
 731        `aiobungie.NotFound`
 732            The requested clan was not found.
 733        """
 734        resp = await self.rest.fetch_clan_admins(clan_id)
 735
 736        return self.factory.deserialize_clan_members(resp)
 737
 738    async def fetch_groups_for_member(
 739        self,
 740        member_id: int,
 741        member_type: typedefs.IntAnd[enums.MembershipType],
 742        /,
 743        *,
 744        filter: int = 0,
 745        group_type: enums.GroupType = enums.GroupType.CLAN,
 746    ) -> collections.Sequence[clans.GroupMember]:
 747        """Fetch information about the groups that a given member has joined.
 748
 749        Parameters
 750        ----------
 751        member_id : `int`
 752            The member's id
 753        member_type : `aiobungie.MembershipType`
 754            The member's membership type.
 755
 756        Other Parameters
 757        ----------------
 758        filter : `int`
 759            Filter apply to list of joined groups. This Default to `0`
 760        group_type : `aiobungie.GroupType`
 761            The group's type.
 762            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 763
 764        Returns
 765        -------
 766        `collections.Sequence[aiobungie.crates.GroupMember]`
 767            A sequence of joined groups for the fetched member.
 768        """
 769        resp = await self.rest.fetch_groups_for_member(
 770            member_id, member_type, filter=filter, group_type=group_type
 771        )
 772
 773        return [
 774            self.factory.deserialize_group_member(group) for group in resp["results"]
 775        ]
 776
 777    async def fetch_potential_groups_for_member(
 778        self,
 779        member_id: int,
 780        member_type: typedefs.IntAnd[enums.MembershipType],
 781        /,
 782        *,
 783        filter: int = 0,
 784        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 785    ) -> collections.Sequence[clans.GroupMember]:
 786        """Fetch the potential groups for a clan member.
 787
 788        Parameters
 789        ----------
 790        member_id : `int`
 791            The member's id
 792        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 793            The member's membership type.
 794
 795        Other Parameters
 796        ----------------
 797        filter : `int`
 798            Filter apply to list of joined groups. This Default to `0`
 799        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
 800            The group's type.
 801            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 802
 803        Returns
 804        -------
 805        `collections.Sequence[aiobungie.crates.GroupMember]`
 806            A sequence of joined potential groups for the fetched member.
 807        """
 808        resp = await self.rest.fetch_potential_groups_for_member(
 809            member_id, member_type, filter=filter, group_type=group_type
 810        )
 811
 812        return [
 813            self.factory.deserialize_group_member(group) for group in resp["results"]
 814        ]
 815
 816    async def fetch_clan_members(
 817        self,
 818        clan_id: int,
 819        /,
 820        *,
 821        name: typing.Optional[str] = None,
 822        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 823    ) -> iterators.Iterator[clans.ClanMember]:
 824        """Fetch Bungie clan members.
 825
 826        Parameters
 827        ----------
 828        clan_id : `int`
 829            The clans id
 830
 831        Other Parameters
 832        ----------------
 833        name : `typing.Optional[str]`
 834            If provided, Only players matching this name will be returned.
 835        type : `aiobungie.MembershipType`
 836            An optional clan member's membership type.
 837            This parameter is used to filter the returned results
 838            by the provided membership, For an example XBox memberships only,
 839            Otherwise will return all memberships.
 840
 841        Returns
 842        -------
 843        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
 844            An iterator over the bungie clan members.
 845
 846        Raises
 847        ------
 848        `aiobungie.NotFound`
 849            The clan was not found.
 850        """
 851        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
 852
 853        return self.factory.deserialize_clan_members(resp)
 854
 855    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
 856        """Fetch the clan banners.
 857
 858        Returns
 859        -------
 860        `collections.Sequence[aiobungie.crates.ClanBanner]`
 861            A sequence of the clan banners.
 862        """
 863        resp = await self.rest.fetch_clan_banners()
 864
 865        return self.factory.deserialize_clan_banners(resp)
 866
 867    # This method is required to be here since it deserialize the clan.
 868    async def kick_clan_member(
 869        self,
 870        access_token: str,
 871        /,
 872        group_id: int,
 873        membership_id: int,
 874        membership_type: typedefs.IntAnd[enums.MembershipType],
 875    ) -> clans.Clan:
 876        """Kick a member from the clan.
 877
 878        .. note::
 879            This request requires OAuth2: oauth2: `AdminGroups` scope.
 880
 881        Parameters
 882        ----------
 883        access_token : `str`
 884            The bearer access token associated with the bungie account.
 885        group_id: `int`
 886            The group id.
 887        membership_id : `int`
 888            The member id to kick.
 889        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 890            The member's membership type.
 891
 892        Returns
 893        -------
 894        `aiobungie.crates.clan.Clan`
 895            The clan that the member was kicked from.
 896        """
 897        resp = await self.rest.kick_clan_member(
 898            access_token,
 899            group_id=group_id,
 900            membership_id=membership_id,
 901            membership_type=membership_type,
 902        )
 903
 904        return self.factory.deserialize_clan(resp)
 905
 906    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
 907        """Fetch a Bungie clan's weekly reward state.
 908
 909        Parameters
 910        ----------
 911        clan_id : `int`
 912            The clan's id.
 913
 914        Returns
 915        -------
 916        `aiobungie.crates.Milestone`
 917            A runtime status of the clan's milestone data.
 918        """
 919
 920        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
 921
 922        return self.factory.deserialize_milestone(resp)
 923
 924    # * Destiny 2 Entities aka Definitions.
 925
 926    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
 927        """Fetch a static inventory item entity given a its hash.
 928
 929        Parameters
 930        ----------
 931        hash: `int`
 932            Inventory item's hash.
 933
 934        Returns
 935        -------
 936        `aiobungie.crates.InventoryEntity`
 937            A bungie inventory item.
 938        """
 939        resp = await self.rest.fetch_inventory_item(hash)
 940
 941        return self.factory.deserialize_inventory_entity(resp)
 942
 943    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
 944        """Fetch a Destiny objective entity given a its hash.
 945
 946        Parameters
 947        ----------
 948        hash: `int`
 949            objective's hash.
 950
 951        Returns
 952        -------
 953        `aiobungie.crates.ObjectiveEntity`
 954            An objective entity item.
 955        """
 956        resp = await self.rest.fetch_objective_entity(hash)
 957
 958        return self.factory.deserialize_objective_entity(resp)
 959
 960    async def search_entities(
 961        self, name: str, entity_type: str, *, page: int = 0
 962    ) -> iterators.Iterator[entity.SearchableEntity]:
 963        """Search for Destiny2 entities given a name and its type.
 964
 965        Parameters
 966        ----------
 967        name : `str`
 968            The name of the entity, i.e., Thunderlord, One thousand voices.
 969        entity_type : `str`
 970            The type of the entity, AKA Definition,
 971            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
 972
 973        Other Parameters
 974        ----------------
 975        page : `int`
 976            An optional page to return. Default to 0.
 977
 978        Returns
 979        -------
 980        `aiobungie.iterators.Iterator[aiobungie.crates.SearchableEntity]`
 981            An iterator over the found results matching the provided name.
 982        """
 983        resp = await self.rest.search_entities(name, entity_type, page=page)
 984
 985        return self.factory.deserialize_inventory_results(resp)
 986
 987    # Fireteams
 988
 989    async def fetch_fireteams(
 990        self,
 991        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
 992        *,
 993        platform: typedefs.IntAnd[
 994            fireteams.FireteamPlatform
 995        ] = fireteams.FireteamPlatform.ANY,
 996        language: typing.Union[
 997            fireteams.FireteamLanguage, str
 998        ] = fireteams.FireteamLanguage.ALL,
 999        date_range: int = 0,
1000        page: int = 0,
1001        slots_filter: int = 0,
1002    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1003        """Fetch public Bungie fireteams with open slots.
1004
1005        Parameters
1006        ----------
1007        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1008            The fireteam activity type.
1009
1010        Other Parameters
1011        ----------------
1012        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1013            If this is provided. Then the results will be filtered with the given platform.
1014            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1015        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1016            A locale language to filter the used language in that fireteam.
1017            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1018        date_range : `int`
1019            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1020        page : `int`
1021            The page number. By default its `0` which returns all available activities.
1022        slots_filter : `int`
1023            Filter the returned fireteams based on available slots. Default is `0`
1024
1025        Returns
1026        -------
1027        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1028            A sequence of `aiobungie.crates.Fireteam` or `None`.
1029        """
1030
1031        resp = await self.rest.fetch_fireteams(
1032            activity_type,
1033            platform=platform,
1034            language=language,
1035            date_range=date_range,
1036            page=page,
1037            slots_filter=slots_filter,
1038        )
1039
1040        return self.factory.deserialize_fireteams(resp)
1041
1042    async def fetch_available_clan_fireteams(
1043        self,
1044        access_token: str,
1045        group_id: int,
1046        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1047        *,
1048        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1049        language: typing.Union[fireteams.FireteamLanguage, str],
1050        date_range: int = 0,
1051        page: int = 0,
1052        public_only: bool = False,
1053        slots_filter: int = 0,
1054    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1055        """Fetch a clan's fireteams with open slots.
1056
1057        .. note::
1058            This method requires OAuth2: ReadGroups scope.
1059
1060        Parameters
1061        ----------
1062        access_token : `str`
1063            The bearer access token associated with the bungie account.
1064        group_id : `int`
1065            The group/clan id of the fireteam.
1066        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1067            The fireteam activity type.
1068
1069        Other Parameters
1070        ----------------
1071        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1072            If this is provided. Then the results will be filtered with the given platform.
1073            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1074        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1075            A locale language to filter the used language in that fireteam.
1076            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1077        date_range : `int`
1078            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1079        page : `int`
1080            The page number. By default its `0` which returns all available activities.
1081        public_only: `bool`
1082            If set to True, Then only public fireteams will be returned.
1083        slots_filter : `int`
1084            Filter the returned fireteams based on available slots. Default is `0`
1085
1086        Returns
1087        -------
1088        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1089            A sequence of  fireteams found in the clan.
1090            `None` will be returned if nothing was found.
1091        """
1092        resp = await self.rest.fetch_available_clan_fireteams(
1093            access_token,
1094            group_id,
1095            activity_type,
1096            platform=platform,
1097            language=language,
1098            date_range=date_range,
1099            page=page,
1100            public_only=public_only,
1101            slots_filter=slots_filter,
1102        )
1103
1104        return self.factory.deserialize_fireteams(resp)
1105
1106    async def fetch_clan_fireteam(
1107        self, access_token: str, fireteam_id: int, group_id: int
1108    ) -> fireteams.AvailableFireteam:
1109        """Fetch a specific clan fireteam.
1110
1111        .. note::
1112            This method requires OAuth2: ReadGroups scope.
1113
1114        Parameters
1115        ----------
1116        access_token : `str`
1117            The bearer access token associated with the bungie account.
1118        group_id : `int`
1119            The group/clan id to fetch the fireteam from.
1120        fireteam_id : `int`
1121            The fireteam id to fetch.
1122
1123        Returns
1124        -------
1125        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1126            A sequence of available fireteams objects if exists. else `None` will be returned.
1127        """
1128        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1129
1130        return self.factory.deserialize_available_fireteams(resp, no_results=True)  # type: ignore[return-value]
1131
1132    async def fetch_my_clan_fireteams(
1133        self,
1134        access_token: str,
1135        group_id: int,
1136        *,
1137        include_closed: bool = True,
1138        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1139        language: typing.Union[fireteams.FireteamLanguage, str],
1140        filtered: bool = True,
1141        page: int = 0,
1142    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1143        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1144
1145        .. note::
1146            This method requires OAuth2: ReadGroups scope.
1147
1148        Parameters
1149        ----------
1150        access_token : str
1151            The bearer access token associated with the bungie account.
1152        group_id : int
1153            The group/clan id to fetch.
1154
1155        Other Parameters
1156        ----------------
1157        include_closed : bool
1158            If provided and set to True, It will also return closed fireteams.
1159            If provided and set to False, It will only return public fireteams. Default is True.
1160        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1161            If this is provided. Then the results will be filtered with the given platform.
1162            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1163        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1164            A locale language to filter the used language in that fireteam.
1165            Defaults to aiobungie.crates.FireteamLanguage.ALL
1166        filtered : bool
1167            If set to True, it will filter by clan. Otherwise not. Default is True.
1168        page : int
1169            The page number. By default its 0 which returns all available activities.
1170
1171        Returns
1172        -------
1173        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1174            A sequence of available fireteams objects if exists. else `None` will be returned.
1175        """
1176        resp = await self.rest.fetch_my_clan_fireteams(
1177            access_token,
1178            group_id,
1179            include_closed=include_closed,
1180            platform=platform,
1181            language=language,
1182            filtered=filtered,
1183            page=page,
1184        )
1185
1186        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]
1187
1188    # Friends and social.
1189
1190    async def fetch_friends(
1191        self, access_token: str, /
1192    ) -> collections.Sequence[friends.Friend]:
1193        """Fetch bungie friend list.
1194
1195        .. note::
1196            This requests OAuth2: ReadUserData scope.
1197
1198        Parameters
1199        -----------
1200        access_token : `str`
1201            The bearer access token associated with the bungie account.
1202
1203        Returns
1204        -------
1205        `collections.Sequence[aiobungie.crates.Friend]`
1206            A sequence of the friends associated with that access token.
1207        """
1208
1209        resp = await self.rest.fetch_friends(access_token)
1210
1211        return self.factory.deserialize_friends(resp)
1212
1213    async def fetch_friend_requests(
1214        self, access_token: str, /
1215    ) -> friends.FriendRequestView:
1216        """Fetch pending bungie friend requests queue.
1217
1218        .. note::
1219            This requests OAuth2: ReadUserData scope.
1220
1221        Parameters
1222        -----------
1223        access_token : `str`
1224            The bearer access token associated with the bungie account.
1225
1226        Returns
1227        -------
1228        `aiobungie.crates.FriendRequestView`
1229            A friend requests view of that associated access token.
1230        """
1231
1232        resp = await self.rest.fetch_friend_requests(access_token)
1233
1234        return self.factory.deserialize_friend_requests(resp)
1235
1236    # Applications and Developer portal.
1237
1238    async def fetch_application(self, appid: int, /) -> application.Application:
1239        """Fetch a Bungie application.
1240
1241        Parameters
1242        -----------
1243        appid: `int`
1244            The application id.
1245
1246        Returns
1247        --------
1248        `aiobungie.crates.Application`
1249            A Bungie application.
1250        """
1251        resp = await self.rest.fetch_application(appid)
1252
1253        return self.factory.deserialize_app(resp)
1254
1255    # Milestones
1256
1257    async def fetch_public_milestone_content(
1258        self, milestone_hash: int, /
1259    ) -> milestones.MilestoneContent:
1260        """Fetch the milestone content given its hash.
1261
1262        Parameters
1263        ----------
1264        milestone_hash : `int`
1265            The milestone hash.
1266
1267        Returns
1268        -------
1269        `aiobungie.crates.milestones.MilestoneContent`
1270            A milestone content object.
1271        """
1272        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1273
1274        return self.factory.deserialize_public_milestone_content(resp)

Standard Bungie API client application.

This client deserialize the REST JSON responses using Factory and returns aiobungie.crates Python object implementations of the responses.

A aiobungie.RESTClient REST client can also be used alone for low-level concepts.

Example
import aiobungie

client = aiobungie.Client('...')

async def main():
    async with client.rest:
        user = await client.fetch_current_user_memberships('...')
        print(user)
Parameters
  • token (str): Your Bungie's API key or Token from the developer's portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
Client( token: str, /, *, client_secret: Optional[str] = None, client_id: Optional[int] = None, dumps: collections.abc.Callable[[dict[str, typing.Any] | list[typing.Any]], bytes] = <function dumps>, loads: collections.abc.Callable[[str | bytes], list[typing.Any] | dict[str, typing.Any]] = <function loads>, max_retries: int = 4)
100    def __init__(
101        self,
102        token: str,
103        /,
104        *,
105        client_secret: typing.Optional[str] = None,
106        client_id: typing.Optional[int] = None,
107        dumps: typedefs.Dumps = helpers.dumps,
108        loads: typedefs.Loads = helpers.loads,
109        max_retries: int = 4,
110    ) -> None:
111        self._rest = rest_.RESTClient(
112            token,
113            dumps=dumps,
114            loads=loads,
115            client_secret=client_secret,
116            client_id=client_id,
117            max_retries=max_retries,
118        )
119
120        self._factory = factory_.Factory(self)
factory: Factory

Returns the marshalling factory for the client.

rest: aiobungie.interfaces.rest.RESTInterface

Returns the REST client for the this client.

request: Client

A readonly ClientApp instance used for external requests.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

def run( self, future: collections.abc.Coroutine[typing.Any, None, None], debug: bool = False) -> None:
138    def run(
139        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
140    ) -> None:
141        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
142        try:
143            if not loop.is_running():
144                loop.set_debug(debug)
145                loop.run_until_complete(future)
146
147        except Exception as exc:
148            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
149
150        except KeyboardInterrupt:
151            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
152            return

Runs a coroutine function until its complete.

This is equivalent to asyncio.get_event_loop().run_until_complete(...)

Parameters
  • future (collections.Coroutine[None, None, None]): A coroutine object.
  • debug (bool): Either to enable asyncio debug or not. Disabled by default.
Example
async def main() -> None:
    await fetch(...)

# Run the coroutine.
client.run(main())
async def fetch_current_user_memberships(self, access_token: str, /) -> aiobungie.crates.user.User:
156    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
157        """Fetch and return a user object of the bungie net user associated with account.
158
159        .. warning::
160            This method requires OAuth2 scope and a Bearer access token.
161
162        Parameters
163        ----------
164        access_token : `str`
165            A valid Bearer access token for the authorization.
166
167        Returns
168        -------
169        `aiobungie.crates.user.User`
170            A user object includes the Destiny memberships and Bungie.net user.
171        """
172        resp = await self.rest.fetch_current_user_memberships(access_token)
173
174        return self.factory.deserialize_user(resp)

Fetch and return a user object of the bungie net user associated with account.

This method requires OAuth2 scope and a Bearer access token.

Parameters
  • access_token (str): A valid Bearer access token for the authorization.
Returns
  • aiobungie.crates.user.User: A user object includes the Destiny memberships and Bungie.net user.
async def fetch_bungie_user(self, id: int, /) -> aiobungie.crates.user.BungieUser:
176    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
177        """Fetch a Bungie user by their BungieNet id.
178
179        .. note::
180            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
181            for other memberships.
182
183        Parameters
184        ----------
185        id: `int`
186            The user id.
187
188        Returns
189        -------
190        `aiobungie.crates.user.BungieUser`
191            A Bungie user.
192
193        Raises
194        ------
195        `aiobungie.error.NotFound`
196            The user was not found.
197        """
198        payload = await self.rest.fetch_bungie_user(id)
199
200        return self.factory.deserialize_bungie_user(payload)

Fetch a Bungie user by their BungieNet id.

This returns a Bungie user membership only. Take a look at Client.fetch_membership_from_id for other memberships.

Parameters
  • id (int): The user id.
Returns
  • aiobungie.crates.user.BungieUser: A Bungie user.
Raises
async def search_users( self, name: str, /) -> Iterator[aiobungie.crates.user.SearchableDestinyUser]:
202    async def search_users(
203        self, name: str, /
204    ) -> iterators.Iterator[user.SearchableDestinyUser]:
205        """Search for players and return all players that matches the same name.
206
207        Parameters
208        ----------
209        name : `buildins.str`
210            The user name.
211
212        Returns
213        -------
214        `aiobungie.iterators.Iterator[aiobungie.crates.DestinyMembership]`
215            A sequence of destiny memberships.
216        """
217        payload = await self.rest.search_users(name)
218
219        return iterators.Iterator(
220            [
221                self.factory.deserialize_searched_user(user)
222                for user in payload["searchResults"]
223            ]
224        )

Search for players and return all players that matches the same name.

Parameters
  • name (buildins.str): The user name.
Returns
async def fetch_user_themes(self) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
226    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
227        """Fetch all available user themes.
228
229        Returns
230        -------
231        `collections.Sequence[aiobungie.crates.user.UserThemes]`
232            A sequence of user themes.
233        """
234        data = await self.rest.fetch_user_themes()
235
236        return self.factory.deserialize_user_themes(data)

Fetch all available user themes.

Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of user themes.
async def fetch_hard_types( self, credential: int, type: Union[int, CredentialType] = <CredentialType.STEAMID: 12>, /) -> aiobungie.crates.user.HardLinkedMembership:
238    async def fetch_hard_types(
239        self,
240        credential: int,
241        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
242        /,
243    ) -> user.HardLinkedMembership:
244        """Gets any hard linked membership given a credential.
245        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
246        Cross Save aware.
247
248        Parameters
249        ----------
250        credential: `int`
251            A valid SteamID64
252        type: `aiobungie.CredentialType`
253            The credential type. This must not be changed
254            Since its only credential that works "currently"
255
256        Returns
257        -------
258        `aiobungie.crates.user.HardLinkedMembership`
259            Information about the hard linked data.
260        """
261
262        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
263
264        return user.HardLinkedMembership(
265            id=int(payload["membershipId"]),
266            type=enums.MembershipType(payload["membershipType"]),
267            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
268        )

Gets any hard linked membership given a credential. Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
  • credential (int): A valid SteamID64
  • type (aiobungie.CredentialType): The credential type. This must not be changed Since its only credential that works "currently"
Returns
  • aiobungie.crates.user.HardLinkedMembership: Information about the hard linked data.
async def fetch_membership_from_id( self, id: int, /, type: Union[int, MembershipType] = <MembershipType.NONE: 0>) -> aiobungie.crates.user.User:
270    async def fetch_membership_from_id(
271        self,
272        id: int,
273        /,
274        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
275    ) -> user.User:
276        """Fetch Bungie user's memberships from their id.
277
278        Notes
279        -----
280        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
281        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
282        see `aiobungie.crates.user.DestinyMembership` for more details.
283        * If you only want the bungie user. Consider using `Client.fetch_user` method.
284
285        Parameters
286        ----------
287        id : `int`
288            The user's id.
289        type : `aiobungie.MembershipType`
290            The user's membership type.
291
292        Returns
293        -------
294        `aiobungie.crates.User`
295            A Bungie user with their membership types.
296
297        Raises
298        ------
299        aiobungie.NotFound
300            The requested user was not found.
301        """
302        payload = await self.rest.fetch_membership_from_id(id, type)
303
304        return self.factory.deserialize_user(payload)

Fetch Bungie user's memberships from their id.

Notes
  • This returns both BungieNet membership and a sequence of the player's DestinyMemberships Which includes Stadia, Xbox, Steam and PSN memberships if the player has them, see aiobungie.crates.user.DestinyMembership for more details.
  • If you only want the bungie user. Consider using Client.fetch_user method.
Parameters
Returns
Raises
async def fetch_user_credentials( self, access_token: str, membership_id: int, /) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
306    async def fetch_user_credentials(
307        self, access_token: str, membership_id: int, /
308    ) -> collections.Sequence[user.UserCredentials]:
309        """Fetch an array of credential types attached to the requested account.
310
311        .. note::
312            This method require OAuth2 Bearer access token.
313
314        Parameters
315        ----------
316        access_token : `str`
317            The bearer access token associated with the bungie account.
318        membership_id : `int`
319            The id of the membership to return.
320
321        Returns
322        -------
323        `collections.Sequence[aiobungie.crates.UserCredentials]`
324            A sequence of the attached user credentials.
325
326        Raises
327        ------
328        `aiobungie.Unauthorized`
329            The access token was wrong or no access token passed.
330        """
331        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
332
333        return self.factory.deserialize_user_credentials(resp)

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def fetch_profile( self, member_id: int, type: Union[int, MembershipType], components: list[ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.Component:
337    async def fetch_profile(
338        self,
339        member_id: int,
340        type: typedefs.IntAnd[enums.MembershipType],
341        components: list[enums.ComponentType],
342        auth: typing.Optional[str] = None,
343    ) -> components.Component:
344        """
345        Fetch a bungie profile passing components to the request.
346
347        Parameters
348        ----------
349        member_id: `int`
350            The member's id.
351        type: `aiobungie.MembershipType`
352            A valid membership type.
353        components : `list[aiobungie.ComponentType]`
354            List of profile components to collect and return.
355
356        Other Parameters
357        ----------------
358        auth : `typing.Optional[str]`
359            A Bearer access_token to make the request with.
360            This is optional and limited to components that only requires an Authorization token.
361
362        Returns
363        --------
364        `aiobungie.crates.Component`
365            A Destiny 2 player profile with its components.
366            Only passed components will be available if they exists. Otherwise they will be `None`
367
368        Raises
369        ------
370        `aiobungie.MembershipTypeError`
371            The provided membership type was invalid.
372        """
373        data = await self.rest.fetch_profile(member_id, type, components, auth)
374        return self.factory.deserialize_components(data)

Fetch a bungie profile passing components to the request.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
  • aiobungie.crates.Component: A Destiny 2 player profile with its components. Only passed components will be available if they exists. Otherwise they will be None
Raises
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, MembershipType], /, *, all: bool = False) -> aiobungie.crates.profile.LinkedProfile:
376    async def fetch_linked_profiles(
377        self,
378        member_id: int,
379        member_type: typedefs.IntAnd[enums.MembershipType],
380        /,
381        *,
382        all: bool = False,
383    ) -> profile.LinkedProfile:
384        """Returns a summary information about all profiles linked to the requested member.
385
386        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
387
388        .. note::
389            It will only return linked accounts whose linkages you are allowed to view.
390
391        Parameters
392        ----------
393        member_id : `int`
394            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
395        member_type : `aiobungie.MembershipType`
396            The type for the membership whose linked Destiny account you want to return.
397
398        Other Parameters
399        ----------------
400        all : `bool`
401            If provided and set to `True`, All memberships regardless
402            of whether they're obscured by overrides will be returned,
403
404            If provided and set to `False`, Only available memberships will be returned.
405            The default for this is `False`.
406
407        Returns
408        -------
409        `aiobungie.crates.profile.LinkedProfile`
410            A linked profile object.
411        """
412        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
413
414        return self.factory.deserialize_linked_profiles(resp)

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
  • member_id (int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
  • member_type (aiobungie.MembershipType): The type for the membership whose linked Destiny account you want to return.
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
  • aiobungie.crates.profile.LinkedProfile: A linked profile object.
async def fetch_player( self, name: str, code: int, /, type: Union[int, MembershipType] = <MembershipType.ALL: -1>) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
416    async def fetch_player(
417        self,
418        name: str,
419        code: int,
420        /,
421        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
422    ) -> collections.Sequence[user.DestinyMembership]:
423        """Fetch a Destiny 2 player's memberships.
424
425        Parameters
426        -----------
427        name: `str`
428            The unique Bungie player name.
429        code : `int`
430            The unique Bungie display name code.
431        type: `aiobungie.internal.enums.MembershipType`
432            The player's membership type, e,g. XBOX, STEAM, PSN
433
434        Returns
435        --------
436        `collections.Sequence[aiobungie.crates.DestinyMembership]`
437            A sequence of the found Destiny 2 player memberships.
438            An empty sequence will be returned if no one found.
439
440        Raises
441        ------
442        `aiobungie.MembershipTypeError`
443            The provided membership type was invalid.
444        """
445        resp = await self.rest.fetch_player(name, code, type)
446
447        return self.factory.deserialize_destiny_memberships(resp)

Fetch a Destiny 2 player's memberships.

Parameters
  • name (str): The unique Bungie player name.
  • code (int): The unique Bungie display name code.
  • type (MembershipType): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
Raises
async def fetch_character( self, member_id: int, membership_type: Union[int, MembershipType], character_id: int, components: list[ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.CharacterComponent:
449    async def fetch_character(
450        self,
451        member_id: int,
452        membership_type: typedefs.IntAnd[enums.MembershipType],
453        character_id: int,
454        components: list[enums.ComponentType],
455        auth: typing.Optional[str] = None,
456    ) -> components.CharacterComponent:
457        """Fetch a Destiny 2 character.
458
459        Parameters
460        ----------
461        member_id: `int`
462            A valid bungie member id.
463        character_id: `int`
464            The Destiny character id to retrieve.
465        membership_type: `aiobungie.internal.enums.MembershipType`
466            The member's membership type.
467        components: `list[aiobungie.ComponentType]`
468            Multiple arguments of character components to collect and return.
469
470        Other Parameters
471        ----------------
472        auth : `typing.Optional[str]`
473            A Bearer access_token to make the request with.
474            This is optional and limited to components that only requires an Authorization token.
475
476        Returns
477        -------
478        `aiobungie.crates.CharacterComponent`
479            A Bungie character component.
480
481        `aiobungie.MembershipTypeError`
482            The provided membership type was invalid.
483        """
484        resp = await self.rest.fetch_character(
485            member_id, membership_type, character_id, components, auth
486        )
487
488        return self.factory.deserialize_character_component(resp)

Fetch a Destiny 2 character.

Parameters
  • member_id (int): A valid bungie member id.
  • character_id (int): The Destiny character id to retrieve.
  • membership_type (MembershipType): The member's membership type.
  • components (list[aiobungie.ComponentType]): Multiple arguments of character components to collect and return.
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, MembershipType]) -> collections.abc.Sequence[aiobungie.crates.activity.ExtendedWeaponValues]:
490    async def fetch_unique_weapon_history(
491        self,
492        membership_id: int,
493        character_id: int,
494        membership_type: typedefs.IntAnd[enums.MembershipType],
495    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
496        """Fetch details about unique weapon usage for a character. Includes all exotics.
497
498        Parameters
499        ----------
500        membership_id : `int`
501            The Destiny user membership id.
502        character_id : `int`
503            The character id to retrieve.
504        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
505            The Destiny user's membership type.
506
507        Returns
508        -------
509        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
510            A sequence of the weapon's extended values.
511        """
512        resp = await self._rest.fetch_unique_weapon_history(
513            membership_id, character_id, membership_type
514        )
515
516        return [
517            self._factory.deserialize_extended_weapon_values(weapon)
518            for weapon in resp["weapons"]
519        ]

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, GameMode], *, membership_type: Union[int, MembershipType] = <MembershipType.ALL: -1>, page: int = 0, limit: int = 250) -> Iterator[aiobungie.crates.activity.Activity]:
523    async def fetch_activities(
524        self,
525        member_id: int,
526        character_id: int,
527        mode: typedefs.IntAnd[enums.GameMode],
528        *,
529        membership_type: typedefs.IntAnd[
530            enums.MembershipType
531        ] = enums.MembershipType.ALL,
532        page: int = 0,
533        limit: int = 250,
534    ) -> iterators.Iterator[activity.Activity]:
535        """Fetch a Destiny 2 activity for the specified character id.
536
537        Parameters
538        ----------
539        member_id: `int`
540            The user id that starts with `4611`.
541        character_id: `int`
542            The id of the character to retrieve the activities for.
543        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
544            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
545
546        Other Parameters
547        ----------------
548        membership_type: `aiobungie.internal.enums.MembershipType`
549            The Member ship type, if nothing was passed than it will return all.
550        page: int
551            The page number. Default is `0`
552        limit: int
553            Limit the returned result. Default is `250`.
554
555        Returns
556        -------
557        `aiobungie.iterators.Iterator[aiobungie.crates.Activity]`
558            An iterator of the player's activities.
559
560        Raises
561        ------
562        `aiobungie.MembershipTypeError`
563            The provided membership type was invalid.
564        """
565        resp = await self.rest.fetch_activities(
566            member_id,
567            character_id,
568            mode,
569            membership_type=membership_type,
570            page=page,
571            limit=limit,
572        )
573
574        return self.factory.deserialize_activities(resp)

Fetch a Destiny 2 activity for the specified character id.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve the activities for.
  • mode (aiobungie.typedefs.IntAnd[GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (MembershipType): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default is 0
  • limit (int): Limit the returned result. Default is 250.
Returns
Raises
async def fetch_post_activity(self, instance_id: int, /) -> aiobungie.crates.activity.PostActivity:
576    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
577        """Fetch a post activity details.
578
579        Parameters
580        ----------
581        instance_id: `int`
582            The activity instance id.
583
584        Returns
585        -------
586        `aiobungie.crates.PostActivity`
587           A post activity object.
588        """
589        resp = await self.rest.fetch_post_activity(instance_id)
590
591        return self.factory.deserialize_post_activity(resp)

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, MembershipType]) -> Iterator[aiobungie.crates.activity.AggregatedActivity]:
593    async def fetch_aggregated_activity_stats(
594        self,
595        character_id: int,
596        membership_id: int,
597        membership_type: typedefs.IntAnd[enums.MembershipType],
598    ) -> iterators.Iterator[activity.AggregatedActivity]:
599        """Fetch aggregated activity stats for a character.
600
601        Parameters
602        ----------
603        character_id: `int`
604            The id of the character to retrieve the activities for.
605        membership_id: `int`
606            The id of the user that started with `4611`.
607        membership_type: `aiobungie.internal.enums.MembershipType`
608            The Member ship type.
609
610        Returns
611        -------
612        `aiobungie.iterators.Iterator[aiobungie.crates.AggregatedActivity]`
613            An iterator of the player's activities.
614
615        Raises
616        ------
617        `aiobungie.MembershipTypeError`
618            The provided membership type was invalid.
619        """
620        resp = await self.rest.fetch_aggregated_activity_stats(
621            character_id, membership_id, membership_type
622        )
623
624        return self.factory.deserialize_aggregated_activities(resp)

Fetch aggregated activity stats for a character.

Parameters
  • character_id (int): The id of the character to retrieve the activities for.
  • membership_id (int): The id of the user that started with 4611.
  • membership_type (MembershipType): The Member ship type.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> aiobungie.crates.clans.Clan:
628    async def fetch_clan_from_id(
629        self,
630        id: int,
631        /,
632        access_token: typing.Optional[str] = None,
633    ) -> clans.Clan:
634        """Fetch a Bungie Clan by its id.
635
636        Parameters
637        -----------
638        id: `int`
639            The clan id.
640
641        Returns
642        --------
643        `aiobungie.crates.Clan`
644            An Bungie clan.
645
646        Raises
647        ------
648        `aiobungie.NotFound`
649            The clan was not found.
650        """
651        resp = await self.rest.fetch_clan_from_id(id, access_token)
652
653        return self.factory.deserialize_clan(resp)

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, GroupType] = <GroupType.CLAN: 1>) -> aiobungie.crates.clans.Clan:
655    async def fetch_clan(
656        self,
657        name: str,
658        /,
659        access_token: typing.Optional[str] = None,
660        *,
661        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
662    ) -> clans.Clan:
663        """Fetch a Clan by its name.
664        This method will return the first clan found with given name.
665
666        Parameters
667        ----------
668        name: `str`
669            The clan name
670
671        Other Parameters
672        ----------------
673        access_token : `typing.Optional[str]`
674            An optional access token to make the request with.
675
676            If the token was bound to a member of the clan,
677            This field `aiobungie.crates.Clan.current_user_membership` will be available
678            and will return the membership of the user who made this request.
679        type : `aiobungie.GroupType`
680            The group type, Default is aiobungie.GroupType.CLAN.
681
682        Returns
683        -------
684        `aiobungie.crates.Clan`
685            A Bungie clan.
686
687        Raises
688        ------
689        `aiobungie.NotFound`
690            The clan was not found.
691        """
692        resp = await self.rest.fetch_clan(name, access_token, type=type)
693
694        return self.factory.deserialize_clan(resp)

Fetch a Clan by its name. This method will return the first clan found with given name.

Parameters
  • name (str): The clan name
Other Parameters
Returns
Raises
async def fetch_clan_conversations( self, clan_id: int, /) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
696    async def fetch_clan_conversations(
697        self, clan_id: int, /
698    ) -> collections.Sequence[clans.ClanConversation]:
699        """Fetch the conversations/chat channels of the given clan id.
700
701        Parameters
702        ----------
703        clan_id : `int`
704            The clan id.
705
706        Returns
707        `collections.Sequence[aiobungie.crates.ClanConversation]`
708            A sequence of the clan chat channels.
709        """
710        resp = await self.rest.fetch_clan_conversations(clan_id)
711
712        return self.factory.deserialize_clan_conversations(resp)

Fetch the conversations/chat channels of the given clan id.

Parameters
async def fetch_clan_admins( self, clan_id: int, /) -> Iterator[aiobungie.crates.clans.ClanMember]:
714    async def fetch_clan_admins(
715        self, clan_id: int, /
716    ) -> iterators.Iterator[clans.ClanMember]:
717        """Fetch the clan founder and admins.
718
719        Parameters
720        ----------
721        clan_id : `int`
722            The clan id.
723
724        Returns
725        -------
726        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
727            An iterator over the found clan admins and founder.
728
729        Raises
730        ------
731        `aiobungie.NotFound`
732            The requested clan was not found.
733        """
734        resp = await self.rest.fetch_clan_admins(clan_id)
735
736        return self.factory.deserialize_clan_members(resp)

Fetch the clan founder and admins.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, MembershipType], /, *, filter: int = 0, group_type: GroupType = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
738    async def fetch_groups_for_member(
739        self,
740        member_id: int,
741        member_type: typedefs.IntAnd[enums.MembershipType],
742        /,
743        *,
744        filter: int = 0,
745        group_type: enums.GroupType = enums.GroupType.CLAN,
746    ) -> collections.Sequence[clans.GroupMember]:
747        """Fetch information about the groups that a given member has joined.
748
749        Parameters
750        ----------
751        member_id : `int`
752            The member's id
753        member_type : `aiobungie.MembershipType`
754            The member's membership type.
755
756        Other Parameters
757        ----------------
758        filter : `int`
759            Filter apply to list of joined groups. This Default to `0`
760        group_type : `aiobungie.GroupType`
761            The group's type.
762            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
763
764        Returns
765        -------
766        `collections.Sequence[aiobungie.crates.GroupMember]`
767            A sequence of joined groups for the fetched member.
768        """
769        resp = await self.rest.fetch_groups_for_member(
770            member_id, member_type, filter=filter, group_type=group_type
771        )
772
773        return [
774            self.factory.deserialize_group_member(group) for group in resp["results"]
775        ]

Fetch information about the groups that a given member has joined.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, MembershipType], /, *, filter: int = 0, group_type: Union[int, GroupType] = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
777    async def fetch_potential_groups_for_member(
778        self,
779        member_id: int,
780        member_type: typedefs.IntAnd[enums.MembershipType],
781        /,
782        *,
783        filter: int = 0,
784        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
785    ) -> collections.Sequence[clans.GroupMember]:
786        """Fetch the potential groups for a clan member.
787
788        Parameters
789        ----------
790        member_id : `int`
791            The member's id
792        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
793            The member's membership type.
794
795        Other Parameters
796        ----------------
797        filter : `int`
798            Filter apply to list of joined groups. This Default to `0`
799        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
800            The group's type.
801            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
802
803        Returns
804        -------
805        `collections.Sequence[aiobungie.crates.GroupMember]`
806            A sequence of joined potential groups for the fetched member.
807        """
808        resp = await self.rest.fetch_potential_groups_for_member(
809            member_id, member_type, filter=filter, group_type=group_type
810        )
811
812        return [
813            self.factory.deserialize_group_member(group) for group in resp["results"]
814        ]

Fetch the potential groups for a clan member.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, MembershipType] = <MembershipType.NONE: 0>) -> Iterator[aiobungie.crates.clans.ClanMember]:
816    async def fetch_clan_members(
817        self,
818        clan_id: int,
819        /,
820        *,
821        name: typing.Optional[str] = None,
822        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
823    ) -> iterators.Iterator[clans.ClanMember]:
824        """Fetch Bungie clan members.
825
826        Parameters
827        ----------
828        clan_id : `int`
829            The clans id
830
831        Other Parameters
832        ----------------
833        name : `typing.Optional[str]`
834            If provided, Only players matching this name will be returned.
835        type : `aiobungie.MembershipType`
836            An optional clan member's membership type.
837            This parameter is used to filter the returned results
838            by the provided membership, For an example XBox memberships only,
839            Otherwise will return all memberships.
840
841        Returns
842        -------
843        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
844            An iterator over the bungie clan members.
845
846        Raises
847        ------
848        `aiobungie.NotFound`
849            The clan was not found.
850        """
851        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
852
853        return self.factory.deserialize_clan_members(resp)

Fetch Bungie clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
  • name (typing.Optional[str]): If provided, Only players matching this name will be returned.
  • type (aiobungie.MembershipType): An optional clan member's membership type. This parameter is used to filter the returned results by the provided membership, For an example XBox memberships only, Otherwise will return all memberships.
Returns
Raises
async def fetch_clan_banners(self) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
855    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
856        """Fetch the clan banners.
857
858        Returns
859        -------
860        `collections.Sequence[aiobungie.crates.ClanBanner]`
861            A sequence of the clan banners.
862        """
863        resp = await self.rest.fetch_clan_banners()
864
865        return self.factory.deserialize_clan_banners(resp)

Fetch the clan banners.

Returns
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, MembershipType]) -> aiobungie.crates.clans.Clan:
868    async def kick_clan_member(
869        self,
870        access_token: str,
871        /,
872        group_id: int,
873        membership_id: int,
874        membership_type: typedefs.IntAnd[enums.MembershipType],
875    ) -> clans.Clan:
876        """Kick a member from the clan.
877
878        .. note::
879            This request requires OAuth2: oauth2: `AdminGroups` scope.
880
881        Parameters
882        ----------
883        access_token : `str`
884            The bearer access token associated with the bungie account.
885        group_id: `int`
886            The group id.
887        membership_id : `int`
888            The member id to kick.
889        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
890            The member's membership type.
891
892        Returns
893        -------
894        `aiobungie.crates.clan.Clan`
895            The clan that the member was kicked from.
896        """
897        resp = await self.rest.kick_clan_member(
898            access_token,
899            group_id=group_id,
900            membership_id=membership_id,
901            membership_type=membership_type,
902        )
903
904        return self.factory.deserialize_clan(resp)

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
  • aiobungie.crates.clan.Clan: The clan that the member was kicked from.
async def fetch_clan_weekly_rewards(self, clan_id: int) -> aiobungie.crates.milestones.Milestone:
906    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
907        """Fetch a Bungie clan's weekly reward state.
908
909        Parameters
910        ----------
911        clan_id : `int`
912            The clan's id.
913
914        Returns
915        -------
916        `aiobungie.crates.Milestone`
917            A runtime status of the clan's milestone data.
918        """
919
920        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
921
922        return self.factory.deserialize_milestone(resp)

Fetch a Bungie clan's weekly reward state.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_inventory_item(self, hash: int, /) -> aiobungie.crates.entity.InventoryEntity:
926    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
927        """Fetch a static inventory item entity given a its hash.
928
929        Parameters
930        ----------
931        hash: `int`
932            Inventory item's hash.
933
934        Returns
935        -------
936        `aiobungie.crates.InventoryEntity`
937            A bungie inventory item.
938        """
939        resp = await self.rest.fetch_inventory_item(hash)
940
941        return self.factory.deserialize_inventory_entity(resp)

Fetch a static inventory item entity given a its hash.

Parameters
  • hash (int): Inventory item's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> aiobungie.crates.entity.ObjectiveEntity:
943    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
944        """Fetch a Destiny objective entity given a its hash.
945
946        Parameters
947        ----------
948        hash: `int`
949            objective's hash.
950
951        Returns
952        -------
953        `aiobungie.crates.ObjectiveEntity`
954            An objective entity item.
955        """
956        resp = await self.rest.fetch_objective_entity(hash)
957
958        return self.factory.deserialize_objective_entity(resp)

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> Iterator[aiobungie.crates.entity.SearchableEntity]:
960    async def search_entities(
961        self, name: str, entity_type: str, *, page: int = 0
962    ) -> iterators.Iterator[entity.SearchableEntity]:
963        """Search for Destiny2 entities given a name and its type.
964
965        Parameters
966        ----------
967        name : `str`
968            The name of the entity, i.e., Thunderlord, One thousand voices.
969        entity_type : `str`
970            The type of the entity, AKA Definition,
971            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
972
973        Other Parameters
974        ----------------
975        page : `int`
976            An optional page to return. Default to 0.
977
978        Returns
979        -------
980        `aiobungie.iterators.Iterator[aiobungie.crates.SearchableEntity]`
981            An iterator over the found results matching the provided name.
982        """
983        resp = await self.rest.search_entities(name, entity_type, page=page)
984
985        return self.factory.deserialize_inventory_results(resp)

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition for emblems, weapons, and other inventory items.
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_fireteams( self, activity_type: Union[int, FireteamActivity], *, platform: Union[int, FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: int = 0, page: int = 0, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
 989    async def fetch_fireteams(
 990        self,
 991        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
 992        *,
 993        platform: typedefs.IntAnd[
 994            fireteams.FireteamPlatform
 995        ] = fireteams.FireteamPlatform.ANY,
 996        language: typing.Union[
 997            fireteams.FireteamLanguage, str
 998        ] = fireteams.FireteamLanguage.ALL,
 999        date_range: int = 0,
1000        page: int = 0,
1001        slots_filter: int = 0,
1002    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1003        """Fetch public Bungie fireteams with open slots.
1004
1005        Parameters
1006        ----------
1007        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1008            The fireteam activity type.
1009
1010        Other Parameters
1011        ----------------
1012        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1013            If this is provided. Then the results will be filtered with the given platform.
1014            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1015        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1016            A locale language to filter the used language in that fireteam.
1017            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1018        date_range : `int`
1019            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1020        page : `int`
1021            The page number. By default its `0` which returns all available activities.
1022        slots_filter : `int`
1023            Filter the returned fireteams based on available slots. Default is `0`
1024
1025        Returns
1026        -------
1027        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1028            A sequence of `aiobungie.crates.Fireteam` or `None`.
1029        """
1030
1031        resp = await self.rest.fetch_fireteams(
1032            activity_type,
1033            platform=platform,
1034            language=language,
1035            date_range=date_range,
1036            page=page,
1037            slots_filter=slots_filter,
1038        )
1039
1040        return self.factory.deserialize_fireteams(resp)

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_available_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, FireteamActivity], *, platform: Union[int, FireteamPlatform], language: Union[FireteamLanguage, str], date_range: int = 0, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
1042    async def fetch_available_clan_fireteams(
1043        self,
1044        access_token: str,
1045        group_id: int,
1046        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1047        *,
1048        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1049        language: typing.Union[fireteams.FireteamLanguage, str],
1050        date_range: int = 0,
1051        page: int = 0,
1052        public_only: bool = False,
1053        slots_filter: int = 0,
1054    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1055        """Fetch a clan's fireteams with open slots.
1056
1057        .. note::
1058            This method requires OAuth2: ReadGroups scope.
1059
1060        Parameters
1061        ----------
1062        access_token : `str`
1063            The bearer access token associated with the bungie account.
1064        group_id : `int`
1065            The group/clan id of the fireteam.
1066        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1067            The fireteam activity type.
1068
1069        Other Parameters
1070        ----------------
1071        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1072            If this is provided. Then the results will be filtered with the given platform.
1073            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1074        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1075            A locale language to filter the used language in that fireteam.
1076            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1077        date_range : `int`
1078            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1079        page : `int`
1080            The page number. By default its `0` which returns all available activities.
1081        public_only: `bool`
1082            If set to True, Then only public fireteams will be returned.
1083        slots_filter : `int`
1084            Filter the returned fireteams based on available slots. Default is `0`
1085
1086        Returns
1087        -------
1088        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1089            A sequence of  fireteams found in the clan.
1090            `None` will be returned if nothing was found.
1091        """
1092        resp = await self.rest.fetch_available_clan_fireteams(
1093            access_token,
1094            group_id,
1095            activity_type,
1096            platform=platform,
1097            language=language,
1098            date_range=date_range,
1099            page=page,
1100            public_only=public_only,
1101            slots_filter=slots_filter,
1102        )
1103
1104        return self.factory.deserialize_fireteams(resp)

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to 0.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
  • typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]: A sequence of fireteams found in the clan. None will be returned if nothing was found.
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> aiobungie.crates.fireteams.AvailableFireteam:
1106    async def fetch_clan_fireteam(
1107        self, access_token: str, fireteam_id: int, group_id: int
1108    ) -> fireteams.AvailableFireteam:
1109        """Fetch a specific clan fireteam.
1110
1111        .. note::
1112            This method requires OAuth2: ReadGroups scope.
1113
1114        Parameters
1115        ----------
1116        access_token : `str`
1117            The bearer access token associated with the bungie account.
1118        group_id : `int`
1119            The group/clan id to fetch the fireteam from.
1120        fireteam_id : `int`
1121            The fireteam id to fetch.
1122
1123        Returns
1124        -------
1125        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1126            A sequence of available fireteams objects if exists. else `None` will be returned.
1127        """
1128        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1129
1130        return self.factory.deserialize_available_fireteams(resp, no_results=True)  # type: ignore[return-value]

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, FireteamPlatform], language: Union[FireteamLanguage, str], filtered: bool = True, page: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]:
1132    async def fetch_my_clan_fireteams(
1133        self,
1134        access_token: str,
1135        group_id: int,
1136        *,
1137        include_closed: bool = True,
1138        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1139        language: typing.Union[fireteams.FireteamLanguage, str],
1140        filtered: bool = True,
1141        page: int = 0,
1142    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1143        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1144
1145        .. note::
1146            This method requires OAuth2: ReadGroups scope.
1147
1148        Parameters
1149        ----------
1150        access_token : str
1151            The bearer access token associated with the bungie account.
1152        group_id : int
1153            The group/clan id to fetch.
1154
1155        Other Parameters
1156        ----------------
1157        include_closed : bool
1158            If provided and set to True, It will also return closed fireteams.
1159            If provided and set to False, It will only return public fireteams. Default is True.
1160        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1161            If this is provided. Then the results will be filtered with the given platform.
1162            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1163        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1164            A locale language to filter the used language in that fireteam.
1165            Defaults to aiobungie.crates.FireteamLanguage.ALL
1166        filtered : bool
1167            If set to True, it will filter by clan. Otherwise not. Default is True.
1168        page : int
1169            The page number. By default its 0 which returns all available activities.
1170
1171        Returns
1172        -------
1173        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1174            A sequence of available fireteams objects if exists. else `None` will be returned.
1175        """
1176        resp = await self.rest.fetch_my_clan_fireteams(
1177            access_token,
1178            group_id,
1179            include_closed=include_closed,
1180            platform=platform,
1181            language=language,
1182            filtered=filtered,
1183            page=page,
1184        )
1185
1186        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]

A method that's similar to fetch_fireteams but requires OAuth2.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_friends( self, access_token: str, /) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
1190    async def fetch_friends(
1191        self, access_token: str, /
1192    ) -> collections.Sequence[friends.Friend]:
1193        """Fetch bungie friend list.
1194
1195        .. note::
1196            This requests OAuth2: ReadUserData scope.
1197
1198        Parameters
1199        -----------
1200        access_token : `str`
1201            The bearer access token associated with the bungie account.
1202
1203        Returns
1204        -------
1205        `collections.Sequence[aiobungie.crates.Friend]`
1206            A sequence of the friends associated with that access token.
1207        """
1208
1209        resp = await self.rest.fetch_friends(access_token)
1210
1211        return self.factory.deserialize_friends(resp)

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> aiobungie.crates.friends.FriendRequestView:
1213    async def fetch_friend_requests(
1214        self, access_token: str, /
1215    ) -> friends.FriendRequestView:
1216        """Fetch pending bungie friend requests queue.
1217
1218        .. note::
1219            This requests OAuth2: ReadUserData scope.
1220
1221        Parameters
1222        -----------
1223        access_token : `str`
1224            The bearer access token associated with the bungie account.
1225
1226        Returns
1227        -------
1228        `aiobungie.crates.FriendRequestView`
1229            A friend requests view of that associated access token.
1230        """
1231
1232        resp = await self.rest.fetch_friend_requests(access_token)
1233
1234        return self.factory.deserialize_friend_requests(resp)

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_application(self, appid: int, /) -> aiobungie.crates.application.Application:
1238    async def fetch_application(self, appid: int, /) -> application.Application:
1239        """Fetch a Bungie application.
1240
1241        Parameters
1242        -----------
1243        appid: `int`
1244            The application id.
1245
1246        Returns
1247        --------
1248        `aiobungie.crates.Application`
1249            A Bungie application.
1250        """
1251        resp = await self.rest.fetch_application(appid)
1252
1253        return self.factory.deserialize_app(resp)

Fetch a Bungie application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_public_milestone_content( self, milestone_hash: int, /) -> aiobungie.crates.milestones.MilestoneContent:
1257    async def fetch_public_milestone_content(
1258        self, milestone_hash: int, /
1259    ) -> milestones.MilestoneContent:
1260        """Fetch the milestone content given its hash.
1261
1262        Parameters
1263        ----------
1264        milestone_hash : `int`
1265            The milestone hash.
1266
1267        Returns
1268        -------
1269        `aiobungie.crates.milestones.MilestoneContent`
1270            A milestone content object.
1271        """
1272        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1273
1274        return self.factory.deserialize_public_milestone_content(resp)

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
  • aiobungie.crates.milestones.MilestoneContent: A milestone content object.
@typing.final
class ClosedReasons(aiobungie.Flag):
771@typing.final
772class ClosedReasons(Flag):
773    """A Flags enumeration representing the reasons why a person can't join this user's fireteam."""
774
775    NONE = 0
776    MATCHMAKING = 1 << 0
777    LOADING = 1 << 1
778    SOLO = 1 << 2
779    """The activity is required to be played solo."""
780    INTERNAL_REASONS = 1 << 3
781    """
782    The user can't be joined for one of a variety of internal reasons.
783    Basically, the game can't let you join at this time,
784    but for reasons that aren't under the control of this user
785    """
786    DISALLOWED_BY_GAME_STATE = 1 << 4
787    """The user's current activity/quest/other transitory game state is preventing joining."""
788    OFFLINE = 32768
789    """The user appears offline."""

A Flags enumeration representing the reasons why a person can't join this user's fireteam.

NONE = <ClosedReasons.NONE: 0>
MATCHMAKING = <ClosedReasons.MATCHMAKING: 1>
LOADING = <ClosedReasons.LOADING: 2>
SOLO = <ClosedReasons.SOLO: 4>

The activity is required to be played solo.

INTERNAL_REASONS = <ClosedReasons.INTERNAL_REASONS: 8>

The user can't be joined for one of a variety of internal reasons. Basically, the game can't let you join at this time, but for reasons that aren't under the control of this user

DISALLOWED_BY_GAME_STATE = <ClosedReasons.DISALLOWED_BY_GAME_STATE: 16>

The user's current activity/quest/other transitory game state is preventing joining.

OFFLINE = <ClosedReasons.OFFLINE: 32768>

The user appears offline.

Inherited Members
Flag
name
value
@typing.final
class ComponentFields(aiobungie.Enum):
74@typing.final
75class ComponentFields(enums.Enum):
76    """An enum that provides fields found in a base component response."""
77
78    PRIVACY = ComponentPrivacy.NONE
79    DISABLED = False

An enum that provides fields found in a base component response.

PRIVACY = <ComponentFields.PRIVACY: NONE>
DISABLED = <ComponentFields.PRIVACY: NONE>
Inherited Members
Enum
name
value
@typing.final
class ComponentPrivacy(builtins.int, aiobungie.Enum):
65@typing.final
66class ComponentPrivacy(int, enums.Enum):
67    """An enum the provides privacy settings for profile components."""
68
69    NONE = 0
70    PUBLIC = 1
71    PRIVATE = 2

An enum the provides privacy settings for profile components.

NONE = <ComponentPrivacy.NONE: 0>
PUBLIC = <ComponentPrivacy.PUBLIC: 1>
PRIVATE = <ComponentPrivacy.PRIVATE: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ComponentType(aiobungie.Enum):
348@typing.final
349class ComponentType(Enum):
350    """An Enum for Destiny 2 profile Components."""
351
352    NONE = 0
353
354    PROFILE = 100
355    PROFILE_INVENTORIES = 102
356    PROFILE_CURRENCIES = 103
357    PROFILE_PROGRESSION = 104
358    ALL_PROFILES = (
359        PROFILE,
360        PROFILE_INVENTORIES,
361        PROFILE_CURRENCIES,
362        PROFILE_PROGRESSION,
363    )
364    """All profile components."""
365
366    VENDORS = 400
367    VENDOR_SALES = 402
368    VENDOR_RECEIPTS = 101
369    ALL_VENDORS = (VENDORS, VENDOR_RECEIPTS, VENDOR_SALES)
370    """All vendor components."""
371
372    # Items
373    ITEM_INSTANCES = 300
374    ITEM_OBJECTIVES = 301
375    ITEM_PERKS = 302
376    ITEM_RENDER_DATA = 303
377    ITEM_STATS = 304
378    ITEM_SOCKETS = 305
379    ITEM_TALENT_GRINDS = 306
380    ITEM_PLUG_STATES = 308
381    ITEM_PLUG_OBJECTIVES = 309
382    ITEM_REUSABLE_PLUGS = 310
383
384    ALL_ITEMS = (
385        ITEM_PLUG_OBJECTIVES,
386        ITEM_PLUG_STATES,
387        ITEM_SOCKETS,
388        ITEM_INSTANCES,
389        ITEM_OBJECTIVES,
390        ITEM_PERKS,
391        ITEM_RENDER_DATA,
392        ITEM_STATS,
393        ITEM_TALENT_GRINDS,
394        ITEM_REUSABLE_PLUGS,
395    )
396    """All item components."""
397
398    PLATFORM_SILVER = 105
399    KIOSKS = 500
400    CURRENCY_LOOKUPS = 600
401    PRESENTATION_NODES = 700
402    COLLECTIBLES = 800
403    RECORDS = 900
404    TRANSITORY = 1000
405    METRICS = 1100
406    INVENTORIES = 102
407    STRING_VARIABLES = 1200
408    CRAFTABLES = 1300
409
410    CHARACTERS = 200
411    CHARACTER_INVENTORY = 201
412    CHARECTER_PROGRESSION = 202
413    CHARACTER_RENDER_DATA = 203
414    CHARACTER_ACTIVITIES = 204
415    CHARACTER_EQUIPMENT = 205
416    CHARACTER_LOADOUTS = 206
417
418    ALL_CHARACTERS = (
419        CHARACTERS,
420        CHARACTER_INVENTORY,
421        CHARECTER_PROGRESSION,
422        CHARACTER_RENDER_DATA,
423        CHARACTER_ACTIVITIES,
424        CHARACTER_EQUIPMENT,
425        CHARACTER_LOADOUTS,
426        RECORDS,
427    )
428    """All character components."""
429
430    ALL = (
431        *ALL_PROFILES,  # type: ignore
432        *ALL_CHARACTERS,  # type: ignore
433        *ALL_VENDORS,  # type: ignore
434        *ALL_ITEMS,  # type: ignore
435        RECORDS,
436        CURRENCY_LOOKUPS,
437        PRESENTATION_NODES,
438        COLLECTIBLES,
439        KIOSKS,
440        METRICS,
441        PLATFORM_SILVER,
442        INVENTORIES,
443        STRING_VARIABLES,
444        TRANSITORY,
445        CRAFTABLES,
446    )
447    """ALl components included."""

An Enum for Destiny 2 profile Components.

NONE = <ComponentType.NONE: 0>
PROFILE = <ComponentType.PROFILE: 100>
PROFILE_INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
PROFILE_CURRENCIES = <ComponentType.PROFILE_CURRENCIES: 103>
PROFILE_PROGRESSION = <ComponentType.PROFILE_PROGRESSION: 104>
ALL_PROFILES = <ComponentType.ALL_PROFILES: (100, 102, 103, 104)>

All profile components.

VENDORS = <ComponentType.VENDORS: 400>
VENDOR_SALES = <ComponentType.VENDOR_SALES: 402>
VENDOR_RECEIPTS = <ComponentType.VENDOR_RECEIPTS: 101>
ALL_VENDORS = <ComponentType.ALL_VENDORS: (400, 101, 402)>

All vendor components.

ITEM_INSTANCES = <ComponentType.ITEM_INSTANCES: 300>
ITEM_OBJECTIVES = <ComponentType.ITEM_OBJECTIVES: 301>
ITEM_PERKS = <ComponentType.ITEM_PERKS: 302>
ITEM_RENDER_DATA = <ComponentType.ITEM_RENDER_DATA: 303>
ITEM_STATS = <ComponentType.ITEM_STATS: 304>
ITEM_SOCKETS = <ComponentType.ITEM_SOCKETS: 305>
ITEM_TALENT_GRINDS = <ComponentType.ITEM_TALENT_GRINDS: 306>
ITEM_PLUG_STATES = <ComponentType.ITEM_PLUG_STATES: 308>
ITEM_PLUG_OBJECTIVES = <ComponentType.ITEM_PLUG_OBJECTIVES: 309>
ITEM_REUSABLE_PLUGS = <ComponentType.ITEM_REUSABLE_PLUGS: 310>
ALL_ITEMS = <ComponentType.ALL_ITEMS: (309, 308, 305, 300, 301, 302, 303, 304, 306, 310)>

All item components.

PLATFORM_SILVER = <ComponentType.PLATFORM_SILVER: 105>
KIOSKS = <ComponentType.KIOSKS: 500>
CURRENCY_LOOKUPS = <ComponentType.CURRENCY_LOOKUPS: 600>
PRESENTATION_NODES = <ComponentType.PRESENTATION_NODES: 700>
COLLECTIBLES = <ComponentType.COLLECTIBLES: 800>
RECORDS = <ComponentType.RECORDS: 900>
TRANSITORY = <ComponentType.TRANSITORY: 1000>
METRICS = <ComponentType.METRICS: 1100>
INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
STRING_VARIABLES = <ComponentType.STRING_VARIABLES: 1200>
CRAFTABLES = <ComponentType.CRAFTABLES: 1300>
CHARACTERS = <ComponentType.CHARACTERS: 200>
CHARACTER_INVENTORY = <ComponentType.CHARACTER_INVENTORY: 201>
CHARECTER_PROGRESSION = <ComponentType.CHARECTER_PROGRESSION: 202>
CHARACTER_RENDER_DATA = <ComponentType.CHARACTER_RENDER_DATA: 203>
CHARACTER_ACTIVITIES = <ComponentType.CHARACTER_ACTIVITIES: 204>
CHARACTER_EQUIPMENT = <ComponentType.CHARACTER_EQUIPMENT: 205>
CHARACTER_LOADOUTS = <ComponentType.CHARACTER_LOADOUTS: 206>
ALL_CHARACTERS = <ComponentType.ALL_CHARACTERS: (200, 201, 202, 203, 204, 205, 206, 900)>

All character components.

ALL = <ComponentType.ALL: (100, 102, 103, 104, 200, 201, 202, 203, 204, 205, 206, 900, 400, 101, 402, 309, 308, 305, 300, 301, 302, 303, 304, 306, 310, 900, 600, 700, 800, 500, 1100, 105, 102, 1200, 1000, 1300)>

ALl components included.

Inherited Members
Enum
name
value
@typing.final
class CredentialType(builtins.int, aiobungie.Enum):
653@typing.final
654class CredentialType(int, Enum):
655    """The types of the accounts system supports at bungie."""
656
657    NONE = 0
658    XUID = 1
659    PSNID = 2
660    WILD = 3
661    FAKE = 4
662    FACEBOOK = 5
663    GOOGLE = 8
664    WINDOWS = 9
665    DEMONID = 10
666    STEAMID = 12
667    BATTLENETID = 14
668    STADIAID = 16
669    TWITCHID = 18

The types of the accounts system supports at bungie.

NONE = <CredentialType.NONE: 0>
XUID = <CredentialType.XUID: 1>
PSNID = <CredentialType.PSNID: 2>
WILD = <CredentialType.WILD: 3>
FAKE = <CredentialType.FAKE: 4>
FACEBOOK = <CredentialType.FACEBOOK: 5>
GOOGLE = <CredentialType.GOOGLE: 8>
WINDOWS = <CredentialType.WINDOWS: 9>
DEMONID = <CredentialType.DEMONID: 10>
STEAMID = <CredentialType.STEAMID: 12>
BATTLENETID = <CredentialType.BATTLENETID: 14>
STADIAID = <CredentialType.STADIAID: 16>
TWITCHID = <CredentialType.TWITCHID: 18>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class DamageType(builtins.int, aiobungie.Enum):
531@typing.final
532class DamageType(int, Enum):
533    """Enums for Destiny Damage types"""
534
535    NONE = 0
536    KINETIC = 1
537    ARC = 2
538    SOLAR = 3
539    VOID = 4
540    RAID = 5
541    """This is a special damage type reserved for some raid activity encounters."""
542    STASIS = 6

Enums for Destiny Damage types

NONE = <DamageType.NONE: 0>
KINETIC = <DamageType.KINETIC: 1>
ARC = <DamageType.ARC: 2>
SOLAR = <DamageType.SOLAR: 3>
VOID = <DamageType.VOID: 4>
RAID = <DamageType.RAID: 5>

This is a special damage type reserved for some raid activity encounters.

STASIS = <DamageType.STASIS: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Difficulty(builtins.int, aiobungie.Enum):
64@typing.final
65class Difficulty(int, enums.Enum):
66    """An enum for activities difficulties."""
67
68    TRIVIAL = 0
69    EASY = 1
70    NORMAL = 2
71    CHALLENGING = 3
72    HARD = 4
73    BRAVE = 5
74    ALMOST_IMPOSSIBLE = 6
75    IMPOSSIBLE = 7

An enum for activities difficulties.

TRIVIAL = <Difficulty.TRIVIAL: 0>
EASY = <Difficulty.EASY: 1>
NORMAL = <Difficulty.NORMAL: 2>
CHALLENGING = <Difficulty.CHALLENGING: 3>
HARD = <Difficulty.HARD: 4>
BRAVE = <Difficulty.BRAVE: 5>
ALMOST_IMPOSSIBLE = <Difficulty.ALMOST_IMPOSSIBLE: 6>
IMPOSSIBLE = <Difficulty.IMPOSSIBLE: 7>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Dungeon(builtins.int, aiobungie.Enum):
150@typing.final
151class Dungeon(int, Enum):
152    """An Enum for all available Dungeon/Like missions in Destiny 2."""
153
154    NORMAL_PRESAGE = 2124066889
155    """Normal Presage"""
156
157    MASTER_PRESAGE = 4212753278
158    """Master Presage"""
159
160    HARBINGER = 1738383283
161    """Harbinger"""
162
163    PROPHECY = 4148187374
164    """Prophecy"""
165
166    MASTER_POH = 785700673
167    """Master Pit of Heresy?"""
168
169    LEGEND_POH = 785700678
170    """Legend Pit of Heresy?"""
171
172    POH = 1375089621
173    """Normal Pit of Heresy."""
174
175    SHATTERED = 2032534090
176    """Shattered Throne"""
177
178    GOA_LEGEND = 4078656646
179    """Grasp of Avarice legend."""
180
181    GOA_MASTER = 3774021532
182    """Grasp of Avarice master."""

An Enum for all available Dungeon/Like missions in Destiny 2.

NORMAL_PRESAGE = <Dungeon.NORMAL_PRESAGE: 2124066889>

Normal Presage

MASTER_PRESAGE = <Dungeon.MASTER_PRESAGE: 4212753278>

Master Presage

HARBINGER = <Dungeon.HARBINGER: 1738383283>

Harbinger

PROPHECY = <Dungeon.PROPHECY: 4148187374>

Prophecy

MASTER_POH = <Dungeon.MASTER_POH: 785700673>

Master Pit of Heresy?

LEGEND_POH = <Dungeon.LEGEND_POH: 785700678>

Legend Pit of Heresy?

POH = <Dungeon.POH: 1375089621>

Normal Pit of Heresy.

SHATTERED = <Dungeon.SHATTERED: 2032534090>

Shattered Throne

GOA_LEGEND = <Dungeon.GOA_LEGEND: 4078656646>

Grasp of Avarice legend.

GOA_MASTER = <Dungeon.GOA_MASTER: 3774021532>

Grasp of Avarice master.

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Enum(enum.Enum):
70class Enum(__enum.Enum):
71    """Builtin Python enum with extra handlings."""
72
73    @property
74    def name(self) -> str:  # type: ignore[override]
75        return self._name_
76
77    @property
78    def value(self) -> typing.Any:  # type: ignore[override]
79        return self._value_
80
81    def __str__(self) -> str:
82        return self._name_
83
84    def __repr__(self) -> str:
85        return f"<{type(self).__name__}.{self._name_}: {self._value_!s}>"
86
87    def __int__(self) -> int:
88        return int(self.value)

Builtin Python enum with extra handlings.

name: str

The name of the Enum member.

value: Any

The value of the Enum member.

class Factory(aiobungie.interfaces.factory.FactoryInterface):
  61class Factory(interfaces.FactoryInterface):
  62    """The base deserialization factory class for all aiobungie objects.
  63
  64    This entity factory is used to deserialize JSON responses from the REST client and turning them
  65    into a `aiobungie.crates` Python classes.
  66    """
  67
  68    __slots__ = ("_net",)
  69
  70    def __init__(self, net: traits.Netrunner) -> None:
  71        self._net = net
  72
  73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
  74        return user.BungieUser(
  75            id=int(data["membershipId"]),
  76            created_at=time.clean_date(data["firstAccess"]),
  77            name=data.get("cachedBungieGlobalDisplayName", undefined.UNDEFINED),
  78            is_deleted=data["isDeleted"],
  79            about=data["about"],
  80            updated_at=time.clean_date(data["lastUpdate"]),
  81            psn_name=data.get("psnDisplayName", None),
  82            stadia_name=data.get("stadiaDisplayName", None),
  83            steam_name=data.get("steamDisplayName", None),
  84            twitch_name=data.get("twitchDisplayName", None),
  85            blizzard_name=data.get("blizzardDisplayName", None),
  86            status=data["statusText"],
  87            locale=data["locale"],
  88            picture=assets.Image(path=str(data["profilePicturePath"])),
  89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
  90            unique_name=data.get("uniqueName", None),
  91            theme_id=int(data["profileTheme"]),
  92            show_activity=bool(data["showActivity"]),
  93            theme_name=data["profileThemeName"],
  94            display_title=data["userTitleDisplay"],
  95        )
  96
  97    def deserialize_partial_bungie_user(
  98        self, payload: typedefs.JSONObject
  99    ) -> user.PartialBungieUser:
 100        return user.PartialBungieUser(
 101            net=self._net,
 102            types=[
 103                enums.MembershipType(type_)
 104                for type_ in payload.get("applicableMembershipTypes", [])
 105            ],
 106            name=payload.get("displayName", undefined.UNDEFINED),
 107            id=int(payload["membershipId"]),
 108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 109            is_public=payload["isPublic"],
 110            icon=assets.Image(payload.get("iconPath", "")),
 111            type=enums.MembershipType(payload["membershipType"]),
 112        )
 113
 114    def deserialize_destiny_membership(
 115        self, payload: typedefs.JSONObject
 116    ) -> user.DestinyMembership:
 117        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
 118        if (
 119            raw_name := payload.get("bungieGlobalDisplayName", "")
 120        ) and not typedefs.is_unknown(raw_name):
 121            name = raw_name
 122
 123        return user.DestinyMembership(
 124            net=self._net,
 125            id=int(payload["membershipId"]),
 126            name=name,
 127            code=payload.get("bungieGlobalDisplayNameCode", None),
 128            last_seen_name=payload.get("LastSeenDisplayName")
 129            or payload.get("displayName")  # noqa: W503
 130            or "",  # noqa: W503
 131            type=enums.MembershipType(payload["membershipType"]),
 132            is_public=payload["isPublic"],
 133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 134            icon=assets.Image(payload.get("iconPath", "")),
 135            types=[
 136                enums.MembershipType(type_)
 137                for type_ in payload.get("applicableMembershipTypes", [])
 138            ],
 139        )
 140
 141    def deserialize_destiny_memberships(
 142        self, data: typedefs.JSONArray
 143    ) -> collections.Sequence[user.DestinyMembership]:
 144        return [self.deserialize_destiny_membership(membership) for membership in data]
 145
 146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
 147        primary_membership_id: typing.Optional[int] = None
 148        if raw_primary_id := data.get("primaryMembershipId"):
 149            primary_membership_id = int(raw_primary_id)
 150
 151        return user.User(
 152            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
 153            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
 154            primary_membership_id=primary_membership_id,
 155        )
 156
 157    def deserialize_searched_user(
 158        self, payload: typedefs.JSONObject
 159    ) -> user.SearchableDestinyUser:
 160        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
 161        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
 162            raw_name
 163        ):
 164            name = raw_name
 165
 166        code: typing.Optional[int] = None
 167        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
 168            code = int(raw_code)
 169
 170        bungie_id: typing.Optional[int] = None
 171        if raw_bungie_id := payload.get("bungieNetMembershipId"):
 172            bungie_id = int(raw_bungie_id)
 173
 174        return user.SearchableDestinyUser(
 175            name=name,
 176            code=code,
 177            bungie_id=bungie_id,
 178            memberships=self.deserialize_destiny_memberships(
 179                payload["destinyMemberships"]
 180            ),
 181        )
 182
 183    def deserialize_user_credentials(
 184        self, payload: typedefs.JSONArray
 185    ) -> collections.Sequence[user.UserCredentials]:
 186        return [
 187            user.UserCredentials(
 188                type=enums.CredentialType(int(creds["credentialType"])),
 189                display_name=creds["credentialDisplayName"],
 190                is_public=creds["isPublic"],
 191                self_as_string=creds.get("credentialAsString", undefined.UNDEFINED),
 192            )
 193            for creds in payload
 194        ]
 195
 196    def deserialize_user_themes(
 197        self, payload: typedefs.JSONArray
 198    ) -> collections.Sequence[user.UserThemes]:
 199        return [
 200            user.UserThemes(
 201                id=int(entry["userThemeId"]),
 202                name=entry["userThemeName"]
 203                if "userThemeName" in entry
 204                else undefined.UNDEFINED,
 205                description=entry["userThemeDescription"]
 206                if "userThemeDescription" in entry
 207                else undefined.UNDEFINED,
 208            )
 209            for entry in payload
 210        ]
 211
 212    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
 213        # This is kinda redundant
 214        data = payload
 215
 216        # This is always outside the details.
 217        current_user_map: typing.Optional[
 218            collections.Mapping[str, clans.ClanMember]
 219        ] = None
 220        if raw_current_user_map := payload.get("currentUserMemberMap"):
 221            current_user_map = {
 222                membership_type: self.deserialize_clan_member(membership)
 223                for membership_type, membership in raw_current_user_map.items()
 224            }
 225
 226        try:
 227            data = payload["detail"]
 228        except KeyError:
 229            pass
 230
 231        id = data["groupId"]
 232        name = data["name"]
 233        created_at = data["creationDate"]
 234        member_count = data["memberCount"]
 235        about = data["about"]
 236        motto = data["motto"]
 237        is_public = data["isPublic"]
 238        banner = assets.Image(str(data["bannerPath"]))
 239        avatar = assets.Image(str(data["avatarPath"]))
 240        tags = data["tags"]
 241        type = data["groupType"]
 242
 243        features = data["features"]
 244        features_obj = clans.ClanFeatures(
 245            max_members=features["maximumMembers"],
 246            max_membership_types=features["maximumMembershipsOfGroupType"],
 247            capabilities=features["capabilities"],
 248            membership_types=features["membershipTypes"],
 249            invite_permissions=features["invitePermissionOverride"],
 250            update_banner_permissions=features["updateBannerPermissionOverride"],
 251            update_culture_permissions=features["updateCulturePermissionOverride"],
 252            join_level=features["joinLevel"],
 253        )
 254
 255        information: typedefs.JSONObject = data["clanInfo"]
 256        progression: collections.Mapping[int, progressions.Progression] = {
 257            int(prog_hash): self.deserialize_progressions(prog)
 258            for prog_hash, prog in information["d2ClanProgressions"].items()
 259        }
 260
 261        founder: typedefs.NoneOr[clans.ClanMember] = None
 262        if raw_founder := payload.get("founder"):
 263            founder = self.deserialize_clan_member(raw_founder)
 264
 265        return clans.Clan(
 266            net=self._net,
 267            id=int(id),
 268            name=name,
 269            type=enums.GroupType(type),
 270            created_at=time.clean_date(created_at),
 271            member_count=member_count,
 272            motto=motto,
 273            about=about,
 274            is_public=is_public,
 275            banner=banner,
 276            avatar=avatar,
 277            tags=tags,
 278            features=features_obj,
 279            owner=founder,
 280            progressions=progression,
 281            call_sign=information["clanCallsign"],
 282            banner_data=information["clanBannerData"],
 283            chat_security=data["chatSecurity"],
 284            conversation_id=int(data["conversationId"]),
 285            allow_chat=data["allowChat"],
 286            theme=data["theme"],
 287            current_user_membership=current_user_map,
 288        )
 289
 290    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
 291        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
 292        return clans.ClanMember(
 293            net=self._net,
 294            last_seen_name=destiny_user.last_seen_name,
 295            id=destiny_user.id,
 296            name=destiny_user.name,
 297            icon=destiny_user.icon,
 298            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
 299            group_id=int(data["groupId"]),
 300            joined_at=time.clean_date(data["joinDate"]),
 301            types=destiny_user.types,
 302            is_public=destiny_user.is_public,
 303            type=destiny_user.type,
 304            code=destiny_user.code,
 305            is_online=data["isOnline"],
 306            crossave_override=destiny_user.crossave_override,
 307            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
 308            if "bungieNetUserInfo" in data
 309            else None,
 310            member_type=enums.ClanMemberType(int(data["memberType"])),
 311        )
 312
 313    def deserialize_clan_members(
 314        self, data: typedefs.JSONObject, /
 315    ) -> iterators.Iterator[clans.ClanMember]:
 316        return iterators.Iterator(
 317            [self.deserialize_clan_member(member) for member in data["results"]]
 318        )
 319
 320    def deserialize_group_member(
 321        self, payload: typedefs.JSONObject
 322    ) -> clans.GroupMember:
 323        member = payload["member"]
 324        return clans.GroupMember(
 325            net=self._net,
 326            join_date=time.clean_date(member["joinDate"]),
 327            group_id=int(member["groupId"]),
 328            member_type=enums.ClanMemberType(member["memberType"]),
 329            is_online=member["isOnline"],
 330            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
 331            inactive_memberships=payload.get("areAllMembershipsInactive", None),
 332            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
 333            group=self.deserialize_clan(payload["group"]),
 334        )
 335
 336    def _deserialize_clan_conversation(
 337        self, payload: typedefs.JSONObject
 338    ) -> clans.ClanConversation:
 339        return clans.ClanConversation(
 340            net=self._net,
 341            id=int(payload["conversationId"]),
 342            group_id=int(payload["groupId"]),
 343            name=(
 344                payload["chatName"]
 345                if not typedefs.is_unknown(payload["chatName"])
 346                else undefined.UNDEFINED
 347            ),
 348            chat_enabled=payload["chatEnabled"],
 349            security=payload["chatSecurity"],
 350        )
 351
 352    def deserialize_clan_conversations(
 353        self, payload: typedefs.JSONArray
 354    ) -> collections.Sequence[clans.ClanConversation]:
 355        return [self._deserialize_clan_conversation(conv) for conv in payload]
 356
 357    def deserialize_app_owner(
 358        self, payload: typedefs.JSONObject
 359    ) -> application.ApplicationOwner:
 360        return application.ApplicationOwner(
 361            net=self._net,
 362            name=payload.get("bungieGlobalDisplayName", undefined.UNDEFINED),
 363            id=int(payload["membershipId"]),
 364            type=enums.MembershipType(payload["membershipType"]),
 365            icon=assets.Image(str(payload["iconPath"])),
 366            is_public=payload["isPublic"],
 367            code=payload.get("bungieGlobalDisplayNameCode", None),
 368        )
 369
 370    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
 371        return application.Application(
 372            id=int(payload["applicationId"]),
 373            name=payload["name"],
 374            link=payload["link"],
 375            status=payload["status"],
 376            redirect_url=payload.get("redirectUrl", None),
 377            created_at=time.clean_date(str(payload["creationDate"])),
 378            published_at=time.clean_date(str(payload["firstPublished"])),
 379            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
 380            scope=payload.get("scope", undefined.UNDEFINED),
 381        )
 382
 383    def _set_character_attrs(self, payload: typedefs.JSONObject) -> character.Character:
 384        return character.Character(
 385            net=self._net,
 386            id=int(payload["characterId"]),
 387            gender=enums.Gender(payload["genderType"]),
 388            race=enums.Race(payload["raceType"]),
 389            class_type=enums.Class(payload["classType"]),
 390            emblem=assets.Image(str(payload["emblemBackgroundPath"])),
 391            emblem_icon=assets.Image(str(payload["emblemPath"])),
 392            emblem_hash=int(payload["emblemHash"]),
 393            last_played=time.clean_date(payload["dateLastPlayed"]),
 394            total_played_time=int(payload["minutesPlayedTotal"]),
 395            member_id=int(payload["membershipId"]),
 396            member_type=enums.MembershipType(payload["membershipType"]),
 397            level=payload["baseCharacterLevel"],
 398            title_hash=payload.get("titleRecordHash", None),
 399            light=payload["light"],
 400            stats={enums.Stat(int(k)): v for k, v in payload["stats"].items()},
 401        )
 402
 403    def deserialize_profile(self, payload: typedefs.JSONObject, /) -> profile.Profile:
 404        payload = payload["data"]
 405        id = int(payload["userInfo"]["membershipId"])
 406        name = payload["userInfo"]["displayName"]
 407        is_public = payload["userInfo"]["isPublic"]
 408        type = enums.MembershipType(payload["userInfo"]["membershipType"])
 409        last_played = time.clean_date(str(payload["dateLastPlayed"]))
 410        character_ids = [int(cid) for cid in payload["characterIds"]]
 411        power_cap = payload["currentSeasonRewardPowerCap"]
 412
 413        return profile.Profile(
 414            id=int(id),
 415            name=name,
 416            is_public=is_public,
 417            type=type,
 418            last_played=last_played,
 419            character_ids=character_ids,
 420            power_cap=power_cap,
 421            net=self._net,
 422        )
 423
 424    def deserialize_profile_item(
 425        self, payload: typedefs.JSONObject
 426    ) -> profile.ProfileItemImpl:
 427        instance_id: typing.Optional[int] = None
 428        if raw_instance_id := payload.get("itemInstanceId"):
 429            instance_id = int(raw_instance_id)
 430
 431        version_number: typing.Optional[int] = None
 432        if raw_version := payload.get("versionNumber"):
 433            version_number = int(raw_version)
 434
 435        transfer_status = enums.TransferStatus(payload["transferStatus"])
 436
 437        return profile.ProfileItemImpl(
 438            net=self._net,
 439            hash=payload["itemHash"],
 440            quantity=payload["quantity"],
 441            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
 442            location=enums.ItemLocation(payload["location"]),
 443            bucket=payload["bucketHash"],
 444            transfer_status=transfer_status,
 445            lockable=payload["lockable"],
 446            state=enums.ItemState(payload["state"]),
 447            dismantle_permissions=payload["dismantlePermission"],
 448            is_wrapper=payload["isWrapper"],
 449            instance_id=instance_id,
 450            version_number=version_number,
 451            ornament_id=payload.get("overrideStyleItemHash"),
 452        )
 453
 454    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
 455        return records.Objective(
 456            net=self._net,
 457            hash=payload["objectiveHash"],
 458            visible=payload["visible"],
 459            complete=payload["complete"],
 460            completion_value=payload["completionValue"],
 461            progress=payload.get("progress"),
 462            destination_hash=payload.get("destinationHash"),
 463            activity_hash=payload.get("activityHash"),
 464        )
 465
 466    def deserialize_records(
 467        self,
 468        payload: typedefs.JSONObject,
 469        scores: typing.Optional[records.RecordScores] = None,
 470        **nodes: int,
 471    ) -> records.Record:
 472        objectives: typing.Optional[list[records.Objective]] = None
 473        interval_objectives: typing.Optional[list[records.Objective]] = None
 474        record_state: typedefs.IntAnd[records.RecordState]
 475
 476        record_state = records.RecordState(payload["state"])
 477
 478        if raw_objs := payload.get("objectives"):
 479            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
 480
 481        if raw_interval_objs := payload.get("intervalObjectives"):
 482            interval_objectives = [
 483                self.deserialize_objectives(obj) for obj in raw_interval_objs
 484            ]
 485
 486        return records.Record(
 487            scores=scores,
 488            categories_node_hash=nodes.get("categories_hash", undefined.UNDEFINED),
 489            seals_node_hash=nodes.get("seals_hash", undefined.UNDEFINED),
 490            state=record_state,
 491            objectives=objectives,
 492            interval_objectives=interval_objectives,
 493            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 494            completion_times=payload.get("completedCount", None),
 495            reward_visibility=payload.get("rewardVisibility"),
 496        )
 497
 498    def deserialize_character_records(
 499        self,
 500        payload: typedefs.JSONObject,
 501        scores: typing.Optional[records.RecordScores] = None,
 502        record_hashes: typing.Optional[list[int]] = None,
 503    ) -> records.CharacterRecord:
 504        record = self.deserialize_records(payload, scores)
 505        return records.CharacterRecord(
 506            scores=scores,
 507            categories_node_hash=record.categories_node_hash,
 508            seals_node_hash=record.seals_node_hash,
 509            state=record.state,
 510            objectives=record.objectives,
 511            interval_objectives=record.interval_objectives,
 512            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 513            completion_times=payload.get("completedCount"),
 514            reward_visibility=payload.get("rewardVisibility"),
 515            record_hashes=record_hashes or [],
 516        )
 517
 518    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
 519        return character.Dye(
 520            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
 521        )
 522
 523    def deserialize_character_customization(
 524        self, payload: typedefs.JSONObject
 525    ) -> character.CustomizationOptions:
 526        return character.CustomizationOptions(
 527            personality=payload["personality"],
 528            face=payload["face"],
 529            skin_color=payload["skinColor"],
 530            lip_color=payload["lipColor"],
 531            eye_color=payload["eyeColor"],
 532            hair_colors=payload.get("hairColors", []),
 533            feature_colors=payload.get("featureColors", []),
 534            decal_color=payload["decalColor"],
 535            wear_helmet=payload["wearHelmet"],
 536            hair_index=payload["hairIndex"],
 537            feature_index=payload["featureIndex"],
 538            decal_index=payload["decalIndex"],
 539        )
 540
 541    def deserialize_character_minimal_equipments(
 542        self, payload: typedefs.JSONObject
 543    ) -> character.MinimalEquipments:
 544        dyes = None
 545        if raw_dyes := payload.get("dyes"):
 546            if raw_dyes:
 547                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
 548        return character.MinimalEquipments(
 549            net=self._net, item_hash=payload["itemHash"], dyes=dyes
 550        )
 551
 552    def deserialize_character_render_data(
 553        self, payload: typedefs.JSONObject, /
 554    ) -> character.RenderedData:
 555        return character.RenderedData(
 556            net=self._net,
 557            customization=self.deserialize_character_customization(
 558                payload["customization"]
 559            ),
 560            custom_dyes=[
 561                self.deserialize_character_dye(dye)
 562                for dye in payload["customDyes"]
 563                if dye
 564            ],
 565            equipment=[
 566                self.deserialize_character_minimal_equipments(equipment)
 567                for equipment in payload["peerView"]["equipment"]
 568            ],
 569        )
 570
 571    def deserialize_available_activity(
 572        self, payload: typedefs.JSONObject
 573    ) -> activity.AvailableActivity:
 574        return activity.AvailableActivity(
 575            hash=payload["activityHash"],
 576            is_new=payload["isNew"],
 577            is_completed=payload["isCompleted"],
 578            is_visible=payload["isVisible"],
 579            display_level=payload.get("displayLevel"),
 580            recommended_light=payload.get("recommendedLight"),
 581            difficulty=activity.Difficulty(payload["difficultyTier"]),
 582            can_join=payload["canJoin"],
 583            can_lead=payload["canLead"],
 584        )
 585
 586    def deserialize_character_activity(
 587        self, payload: typedefs.JSONObject
 588    ) -> activity.CharacterActivity:
 589        current_mode: typing.Optional[enums.GameMode] = None
 590        if raw_current_mode := payload.get("currentActivityModeType"):
 591            current_mode = enums.GameMode(raw_current_mode)
 592
 593        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
 594        if raw_current_modes := payload.get("currentActivityModeTypes"):
 595            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
 596
 597        return activity.CharacterActivity(
 598            date_started=time.clean_date(payload["dateActivityStarted"]),
 599            current_hash=payload["currentActivityHash"],
 600            current_mode_hash=payload["currentActivityModeHash"],
 601            current_mode=current_mode,
 602            current_mode_hashes=payload.get("currentActivityModeHashes"),
 603            current_mode_types=current_mode_types,
 604            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
 605            last_story_hash=payload["lastCompletedStoryHash"],
 606            available_activities=[
 607                self.deserialize_available_activity(activity_)
 608                for activity_ in payload["availableActivities"]
 609            ],
 610        )
 611
 612    def deserialize_profile_items(
 613        self, payload: typedefs.JSONObject, /
 614    ) -> list[profile.ProfileItemImpl]:
 615        return [self.deserialize_profile_item(item) for item in payload["items"]]
 616
 617    def _deserialize_node(self, payload: typedefs.JSONObject) -> records.Node:
 618        return records.Node(
 619            state=int(payload["state"]),
 620            objective=self.deserialize_objectives(payload["objective"])
 621            if "objective" in payload
 622            else None,
 623            progress_value=int(payload["progressValue"]),
 624            completion_value=int(payload["completionValue"]),
 625            record_category_score=int(payload["recordCategoryScore"])
 626            if "recordCategoryScore" in payload
 627            else None,
 628        )
 629
 630    @staticmethod
 631    def _deserialize_collectible(payload: typedefs.JSONObject) -> items.Collectible:
 632        recent_collectibles: typing.Optional[collections.Collection[int]] = None
 633        if raw_recent_collectibles := payload.get("recentCollectibleHashes"):
 634            recent_collectibles = [
 635                int(item_hash) for item_hash in raw_recent_collectibles
 636            ]
 637
 638        collectibles: dict[int, int] = {}
 639        for item_hash, mapping in payload["collectibles"].items():
 640            collectibles[int(item_hash)] = int(mapping["state"])
 641
 642        return items.Collectible(
 643            recent_collectibles=recent_collectibles,
 644            collectibles=collectibles,
 645            collection_category_hash=int(payload["collectionCategoriesRootNodeHash"]),
 646            collection_badges_hash=int(payload["collectionBadgesRootNodeHash"]),
 647        )
 648
 649    @staticmethod
 650    def _deserialize_currencies(
 651        payload: typedefs.JSONObject,
 652    ) -> collections.Sequence[items.Currency]:
 653        return [
 654            items.Currency(hash=int(item_hash), amount=int(amount))
 655            for item_hash, amount in payload["itemQuantities"].items()
 656        ]
 657
 658    def deserialize_progressions(
 659        self, payload: typedefs.JSONObject
 660    ) -> progressions.Progression:
 661        return progressions.Progression(
 662            hash=int(payload["progressionHash"]),
 663            level=int(payload["level"]),
 664            cap=int(payload["levelCap"]),
 665            daily_limit=int(payload["dailyLimit"]),
 666            weekly_limit=int(payload["weeklyLimit"]),
 667            current_progress=int(payload["currentProgress"]),
 668            daily_progress=int(payload["dailyProgress"]),
 669            needed=int(payload["progressToNextLevel"]),
 670            next_level=int(payload["nextLevelAt"]),
 671        )
 672
 673    def _deserialize_factions(
 674        self, payload: typedefs.JSONObject
 675    ) -> progressions.Factions:
 676        progs = self.deserialize_progressions(payload)
 677        return progressions.Factions(
 678            hash=progs.hash,
 679            level=progs.level,
 680            cap=progs.cap,
 681            daily_limit=progs.daily_limit,
 682            weekly_limit=progs.weekly_limit,
 683            current_progress=progs.current_progress,
 684            daily_progress=progs.daily_progress,
 685            needed=progs.needed,
 686            next_level=progs.next_level,
 687            faction_hash=payload["factionHash"],
 688            faction_vendor_hash=payload["factionVendorIndex"],
 689        )
 690
 691    def _deserialize_milestone_available_quest(
 692        self, payload: typedefs.JSONObject
 693    ) -> milestones.MilestoneQuest:
 694        return milestones.MilestoneQuest(
 695            item_hash=payload["questItemHash"],
 696            status=self._deserialize_milestone_quest_status(payload["status"]),
 697        )
 698
 699    def _deserialize_milestone_activity(
 700        self, payload: typedefs.JSONObject
 701    ) -> milestones.MilestoneActivity:
 702        phases: typing.Optional[
 703            collections.Sequence[milestones.MilestoneActivityPhase]
 704        ] = None
 705        if raw_phases := payload.get("phases"):
 706            phases = [
 707                milestones.MilestoneActivityPhase(
 708                    is_completed=obj["complete"], hash=obj["phaseHash"]
 709                )
 710                for obj in raw_phases
 711            ]
 712
 713        return milestones.MilestoneActivity(
 714            hash=payload["activityHash"],
 715            challenges=[
 716                self.deserialize_objectives(obj["objective"])
 717                for obj in payload["challenges"]
 718            ],
 719            modifier_hashes=payload.get("modifierHashes"),
 720            boolean_options=payload.get("booleanActivityOptions"),
 721            phases=phases,
 722        )
 723
 724    def _deserialize_milestone_quest_status(
 725        self, payload: typedefs.JSONObject
 726    ) -> milestones.QuestStatus:
 727        return milestones.QuestStatus(
 728            net=self._net,
 729            quest_hash=payload["questHash"],
 730            step_hash=payload["stepHash"],
 731            step_objectives=[
 732                self.deserialize_objectives(objective)
 733                for objective in payload["stepObjectives"]
 734            ],
 735            is_tracked=payload["tracked"],
 736            is_completed=payload["completed"],
 737            started=payload["started"],
 738            item_instance_id=payload["itemInstanceId"],
 739            vendor_hash=payload.get("vendorHash"),
 740            is_redeemed=payload["redeemed"],
 741        )
 742
 743    def _deserialize_milestone_rewards(
 744        self, payload: typedefs.JSONObject
 745    ) -> milestones.MilestoneReward:
 746        return milestones.MilestoneReward(
 747            category_hash=payload["rewardCategoryHash"],
 748            entries=[
 749                milestones.MilestoneRewardEntry(
 750                    entry_hash=entry["rewardEntryHash"],
 751                    is_earned=entry["earned"],
 752                    is_redeemed=entry["redeemed"],
 753                )
 754                for entry in payload["entries"]
 755            ],
 756        )
 757
 758    def deserialize_milestone(
 759        self, payload: typedefs.JSONObject
 760    ) -> milestones.Milestone:
 761        start_date: typing.Optional[datetime.datetime] = None
 762        if raw_start_date := payload.get("startDate"):
 763            start_date = time.clean_date(raw_start_date)
 764
 765        end_date: typing.Optional[datetime.datetime] = None
 766        if raw_end_date := payload.get("endDate"):
 767            end_date = time.clean_date(raw_end_date)
 768
 769        rewards: typing.Optional[
 770            collections.Collection[milestones.MilestoneReward]
 771        ] = None
 772        if raw_rewards := payload.get("rewards"):
 773            rewards = [
 774                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
 775            ]
 776
 777        activities: typing.Optional[
 778            collections.Sequence[milestones.MilestoneActivity]
 779        ] = None
 780        if raw_activities := payload.get("activities"):
 781            activities = [
 782                self._deserialize_milestone_activity(active)
 783                for active in raw_activities
 784            ]
 785
 786        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
 787        if raw_quests := payload.get("availableQuests"):
 788            quests = [
 789                self._deserialize_milestone_available_quest(quest)
 790                for quest in raw_quests
 791            ]
 792
 793        vendors: typing.Optional[
 794            collections.Sequence[milestones.MilestoneVendor]
 795        ] = None
 796        if raw_vendors := payload.get("vendors"):
 797            vendors = [
 798                milestones.MilestoneVendor(
 799                    vendor_hash=vendor["vendorHash"],
 800                    preview_itemhash=vendor.get("previewItemHash"),
 801                )
 802                for vendor in raw_vendors
 803            ]
 804
 805        return milestones.Milestone(
 806            hash=payload["milestoneHash"],
 807            start_date=start_date,
 808            end_date=end_date,
 809            order=payload["order"],
 810            rewards=rewards,
 811            available_quests=quests,
 812            activities=activities,
 813            vendors=vendors,
 814        )
 815
 816    def _deserialize_artifact_tiers(
 817        self, payload: typedefs.JSONObject
 818    ) -> season.ArtifactTier:
 819        return season.ArtifactTier(
 820            hash=payload["tierHash"],
 821            is_unlocked=payload["isUnlocked"],
 822            points_to_unlock=payload["pointsToUnlock"],
 823            items=[
 824                season.ArtifactTierItem(
 825                    hash=item["itemHash"], is_active=item["isActive"]
 826                )
 827                for item in payload["items"]
 828            ],
 829        )
 830
 831    def deserialize_characters(
 832        self, payload: typedefs.JSONObject
 833    ) -> collections.Mapping[int, character.Character]:
 834        return {
 835            int(char_id): self._set_character_attrs(char)
 836            for char_id, char in payload["data"].items()
 837        }
 838
 839    def deserialize_character(
 840        self, payload: typedefs.JSONObject
 841    ) -> character.Character:
 842        return self._set_character_attrs(payload)
 843
 844    def deserialize_character_equipments(
 845        self, payload: typedefs.JSONObject
 846    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
 847        return {
 848            int(char_id): self.deserialize_profile_items(item)
 849            for char_id, item in payload["data"].items()
 850        }
 851
 852    def deserialize_character_activities(
 853        self, payload: typedefs.JSONObject
 854    ) -> collections.Mapping[int, activity.CharacterActivity]:
 855        return {
 856            int(char_id): self.deserialize_character_activity(data)
 857            for char_id, data in payload["data"].items()
 858        }
 859
 860    def deserialize_characters_render_data(
 861        self, payload: typedefs.JSONObject
 862    ) -> collections.Mapping[int, character.RenderedData]:
 863        return {
 864            int(char_id): self.deserialize_character_render_data(data)
 865            for char_id, data in payload["data"].items()
 866        }
 867
 868    def deserialize_character_progressions(
 869        self, payload: typedefs.JSONObject
 870    ) -> character.CharacterProgression:
 871        progressions_ = {
 872            int(prog_id): self.deserialize_progressions(prog)
 873            for prog_id, prog in payload["progressions"].items()
 874        }
 875
 876        factions = {
 877            int(faction_id): self._deserialize_factions(faction)
 878            for faction_id, faction in payload["factions"].items()
 879        }
 880
 881        milestones_ = {
 882            int(milestone_hash): self.deserialize_milestone(milestone)
 883            for milestone_hash, milestone in payload["milestones"].items()
 884        }
 885
 886        uninstanced_item_objectives = {
 887            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
 888            for item_hash, obj in payload["uninstancedItemObjectives"].items()
 889        }
 890
 891        artifact = payload["seasonalArtifact"]
 892        seasonal_artifact = season.CharacterScopedArtifact(
 893            hash=artifact["artifactHash"],
 894            points_used=artifact["pointsUsed"],
 895            reset_count=artifact["resetCount"],
 896            tiers=[
 897                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
 898            ],
 899        )
 900        checklists = payload["checklists"]
 901
 902        return character.CharacterProgression(
 903            progressions=progressions_,
 904            factions=factions,
 905            checklists=checklists,
 906            milestones=milestones_,
 907            seasonal_artifact=seasonal_artifact,
 908            uninstanced_item_objectives=uninstanced_item_objectives,
 909        )
 910
 911    def deserialize_character_progressions_mapping(
 912        self, payload: typedefs.JSONObject
 913    ) -> collections.Mapping[int, character.CharacterProgression]:
 914        character_progressions: collections.Mapping[
 915            int, character.CharacterProgression
 916        ] = {}
 917        for char_id, data in payload["data"].items():
 918            # A little hack to stop mypy complaining about Mapping <-> dict
 919            character_progressions[
 920                int(char_id)
 921            ] = self.deserialize_character_progressions(data)  # type: ignore[index]
 922        return character_progressions
 923
 924    def deserialize_characters_records(
 925        self,
 926        payload: typedefs.JSONObject,
 927    ) -> collections.Mapping[int, records.CharacterRecord]:
 928        return {
 929            int(rec_id): self.deserialize_character_records(
 930                rec, record_hashes=payload.get("featuredRecordHashes")
 931            )
 932            for rec_id, rec in payload["records"].items()
 933        }
 934
 935    def deserialize_profile_records(
 936        self, payload: typedefs.JSONObject
 937    ) -> collections.Mapping[int, records.Record]:
 938        raw_profile_records = payload["data"]
 939        scores = records.RecordScores(
 940            current_score=raw_profile_records["score"],
 941            legacy_score=raw_profile_records["legacyScore"],
 942            lifetime_score=raw_profile_records["lifetimeScore"],
 943        )
 944        return {
 945            int(record_id): self.deserialize_records(
 946                record,
 947                scores,
 948                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
 949                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
 950            )
 951            for record_id, record in raw_profile_records["records"].items()
 952        }
 953
 954    def _deserialize_craftable_socket_plug(
 955        self, payload: typedefs.JSONObject
 956    ) -> items.CraftableSocketPlug:
 957        return items.CraftableSocketPlug(
 958            item_hash=int(payload["plugItemHash"]),
 959            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 960        )
 961
 962    def _deserialize_craftable_socket(
 963        self, payload: typedefs.JSONObject
 964    ) -> items.CraftableSocket:
 965        plugs: list[items.CraftableSocketPlug] = []
 966        if raw_plug := payload.get("plug"):
 967            plugs.extend(
 968                self._deserialize_craftable_socket_plug(plug) for plug in raw_plug
 969            )
 970
 971        return items.CraftableSocket(
 972            plug_set_hash=int(payload["plugSetHash"]), plugs=plugs
 973        )
 974
 975    def _deserialize_craftable_item(
 976        self, payload: typedefs.JSONObject
 977    ) -> items.CraftableItem:
 978        return items.CraftableItem(
 979            is_visible=payload["visible"],
 980            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 981            sockets=[
 982                self._deserialize_craftable_socket(socket)
 983                for socket in payload["sockets"]
 984            ],
 985        )
 986
 987    def deserialize_craftables_component(
 988        self, payload: typedefs.JSONObject
 989    ) -> components.CraftablesComponent:
 990        return components.CraftablesComponent(
 991            net=self._net,
 992            craftables={
 993                int(item_id): self._deserialize_craftable_item(item)
 994                for item_id, item in payload["craftables"].items()
 995                if item is not None
 996            },
 997            crafting_root_node_hash=payload["craftingRootNodeHash"],
 998        )
 999
1000    def deserialize_components(  # noqa: C901 Too complex.
1001        self, payload: typedefs.JSONObject
1002    ) -> components.Component:
1003        profile_: typing.Optional[profile.Profile] = None
1004        if raw_profile := payload.get("profile"):
1005            profile_ = self.deserialize_profile(raw_profile)
1006
1007        profile_progression: typing.Optional[profile.ProfileProgression] = None
1008        if raw_profile_progression := payload.get("profileProgression"):
1009            profile_progression = self.deserialize_profile_progression(
1010                raw_profile_progression
1011            )
1012
1013        profile_currencies: typing.Optional[
1014            collections.Sequence[profile.ProfileItemImpl]
1015        ] = None
1016        if raw_profile_currencies := payload.get("profileCurrencies"):
1017            if "data" in raw_profile_currencies:
1018                profile_currencies = self.deserialize_profile_items(
1019                    raw_profile_currencies["data"]
1020                )
1021
1022        profile_inventories: typing.Optional[
1023            collections.Sequence[profile.ProfileItemImpl]
1024        ] = None
1025        if raw_profile_inventories := payload.get("profileInventory"):
1026            if "data" in raw_profile_inventories:
1027                profile_inventories = self.deserialize_profile_items(
1028                    raw_profile_inventories["data"]
1029                )
1030
1031        profile_records: typing.Optional[
1032            collections.Mapping[int, records.Record]
1033        ] = None
1034
1035        if raw_profile_records_ := payload.get("profileRecords"):
1036            profile_records = self.deserialize_profile_records(raw_profile_records_)
1037
1038        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1039        if raw_characters := payload.get("characters"):
1040            characters = self.deserialize_characters(raw_characters)
1041
1042        character_records: typing.Optional[
1043            collections.Mapping[int, records.CharacterRecord]
1044        ] = None
1045
1046        if raw_character_records := payload.get("characterRecords"):
1047            # Had to do it in two steps..
1048            to_update: typedefs.JSONObject = {}
1049            for _, data in raw_character_records["data"].items():
1050                for record_id, record in data.items():
1051                    to_update[record_id] = record
1052
1053            character_records = {
1054                int(rec_id): self.deserialize_character_records(
1055                    rec, record_hashes=to_update.get("featuredRecordHashes")
1056                )
1057                for rec_id, rec in to_update["records"].items()
1058            }
1059
1060        character_equipments: typing.Optional[
1061            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1062        ] = None
1063        if raw_character_equips := payload.get("characterEquipment"):
1064            character_equipments = self.deserialize_character_equipments(
1065                raw_character_equips
1066            )
1067
1068        character_inventories: typing.Optional[
1069            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1070        ] = None
1071        if raw_character_inventories := payload.get("characterInventories"):
1072            if "data" in raw_character_inventories:
1073                character_inventories = self.deserialize_character_equipments(
1074                    raw_character_inventories
1075                )
1076
1077        character_activities: typing.Optional[
1078            collections.Mapping[int, activity.CharacterActivity]
1079        ] = None
1080        if raw_char_acts := payload.get("characterActivities"):
1081            character_activities = self.deserialize_character_activities(raw_char_acts)
1082
1083        character_render_data: typing.Optional[
1084            collections.Mapping[int, character.RenderedData]
1085        ] = None
1086        if raw_character_render_data := payload.get("characterRenderData"):
1087            character_render_data = self.deserialize_characters_render_data(
1088                raw_character_render_data
1089            )
1090
1091        character_progressions: typing.Optional[
1092            collections.Mapping[int, character.CharacterProgression]
1093        ] = None
1094
1095        if raw_character_progressions := payload.get("characterProgressions"):
1096            character_progressions = self.deserialize_character_progressions_mapping(
1097                raw_character_progressions
1098            )
1099
1100        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1101        if raw_profile_string_vars := payload.get("profileStringVariables"):
1102            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1103
1104        character_string_vars: typing.Optional[
1105            collections.Mapping[int, collections.Mapping[int, int]]
1106        ] = None
1107        if raw_character_string_vars := payload.get("characterStringVariables"):
1108            character_string_vars = {
1109                int(char_id): data["integerValuesByHash"]
1110                for char_id, data in raw_character_string_vars["data"].items()
1111            }
1112
1113        metrics: typing.Optional[
1114            collections.Sequence[
1115                collections.Mapping[
1116                    int, tuple[bool, typing.Optional[records.Objective]]
1117                ]
1118            ]
1119        ] = None
1120        root_node_hash: typing.Optional[int] = None
1121
1122        if raw_metrics := payload.get("metrics"):
1123            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1124            metrics = [
1125                {
1126                    int(metrics_hash): (
1127                        data["invisible"],
1128                        self.deserialize_objectives(data["objectiveProgress"])
1129                        if "objectiveProgress" in data
1130                        else None,
1131                    )
1132                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1133                }
1134            ]
1135        transitory: typing.Optional[fireteams.FireteamParty] = None
1136        if raw_transitory := payload.get("profileTransitoryData"):
1137            if "data" in raw_transitory:
1138                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1139
1140        item_components: typing.Optional[components.ItemsComponent] = None
1141        if raw_item_components := payload.get("itemComponents"):
1142            item_components = self.deserialize_items_component(raw_item_components)
1143
1144        profile_plugsets: typing.Optional[
1145            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1146        ] = None
1147
1148        if raw_profile_plugs := payload.get("profilePlugSets"):
1149            profile_plugsets = {
1150                int(index): [self.deserialize_plug_item_state(state) for state in data]
1151                for index, data in raw_profile_plugs["data"]["plugs"].items()
1152            }
1153
1154        character_plugsets: typing.Optional[
1155            collections.Mapping[
1156                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1157            ]
1158        ] = None
1159        if raw_char_plugsets := payload.get("characterPlugSets"):
1160            character_plugsets = {
1161                int(char_id): {
1162                    int(index): [
1163                        self.deserialize_plug_item_state(state) for state in data
1164                    ]
1165                    for index, data in inner["plugs"].items()
1166                }
1167                for char_id, inner in raw_char_plugsets["data"].items()
1168            }
1169
1170        character_collectibles: typing.Optional[
1171            collections.Mapping[int, items.Collectible]
1172        ] = None
1173        if raw_character_collectibles := payload.get("characterCollectibles"):
1174            character_collectibles = {
1175                int(char_id): self._deserialize_collectible(data)
1176                for char_id, data in raw_character_collectibles["data"].items()
1177            }
1178
1179        profile_collectibles: typing.Optional[items.Collectible] = None
1180        if raw_profile_collectibles := payload.get("profileCollectibles"):
1181            profile_collectibles = self._deserialize_collectible(
1182                raw_profile_collectibles["data"]
1183            )
1184
1185        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1186        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1187            profile_nodes = {
1188                int(node_hash): self._deserialize_node(node)
1189                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1190            }
1191
1192        character_nodes: typing.Optional[
1193            collections.Mapping[int, collections.Mapping[int, records.Node]]
1194        ] = None
1195        if raw_character_nodes := payload.get("characterPresentationNodes"):
1196            character_nodes = {
1197                int(char_id): {
1198                    int(node_hash): self._deserialize_node(node)
1199                    for node_hash, node in each_character["nodes"].items()
1200                }
1201                for char_id, each_character in raw_character_nodes["data"].items()
1202            }
1203
1204        platform_silver: typing.Optional[
1205            collections.Mapping[str, profile.ProfileItemImpl]
1206        ] = None
1207        if raw_platform_silver := payload.get("platformSilver"):
1208            if "data" in raw_platform_silver:
1209                platform_silver = {
1210                    platform_name: self.deserialize_profile_item(item)
1211                    for platform_name, item in raw_platform_silver["data"][
1212                        "platformSilver"
1213                    ].items()
1214                }
1215
1216        character_currency_lookups: typing.Optional[
1217            collections.Mapping[int, collections.Sequence[items.Currency]]
1218        ] = None
1219        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1220            if "data" in raw_char_lookups:
1221                character_currency_lookups = {
1222                    int(char_id): self._deserialize_currencies(currency)
1223                    for char_id, currency in raw_char_lookups["data"].items()
1224                }
1225
1226        character_craftables: typing.Optional[
1227            collections.Mapping[int, components.CraftablesComponent]
1228        ] = None
1229        if raw_character_craftables := payload.get("characterCraftables"):
1230            if "data" in raw_character_craftables:
1231                character_craftables = {
1232                    int(char_id): self.deserialize_craftables_component(craftable)
1233                    for char_id, craftable in raw_character_craftables["data"].items()
1234                }
1235
1236        return components.Component(
1237            profiles=profile_,
1238            profile_progression=profile_progression,
1239            profile_currencies=profile_currencies,
1240            profile_inventories=profile_inventories,
1241            profile_records=profile_records,
1242            characters=characters,
1243            character_records=character_records,
1244            character_equipments=character_equipments,
1245            character_inventories=character_inventories,
1246            character_activities=character_activities,
1247            character_render_data=character_render_data,
1248            character_progressions=character_progressions,
1249            profile_string_variables=profile_string_vars,
1250            character_string_variables=character_string_vars,
1251            metrics=metrics,
1252            root_node_hash=root_node_hash,
1253            transitory=transitory,
1254            item_components=item_components,
1255            profile_plugsets=profile_plugsets,
1256            character_plugsets=character_plugsets,
1257            character_collectibles=character_collectibles,
1258            profile_collectibles=profile_collectibles,
1259            profile_nodes=profile_nodes,
1260            character_nodes=character_nodes,
1261            platform_silver=platform_silver,
1262            character_currency_lookups=character_currency_lookups,
1263            character_craftables=character_craftables,
1264        )
1265
1266    def deserialize_items_component(
1267        self, payload: typedefs.JSONObject
1268    ) -> components.ItemsComponent:
1269        instances: typing.Optional[
1270            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1271        ] = None
1272        if raw_instances := payload.get("instances"):
1273            instances = [
1274                {
1275                    int(ins_id): self.deserialize_instanced_item(item)
1276                    for ins_id, item in raw_instances["data"].items()
1277                }
1278            ]
1279
1280        render_data: typing.Optional[
1281            collections.Mapping[int, tuple[bool, dict[int, int]]]
1282        ] = None
1283        if raw_render_data := payload.get("renderData"):
1284            render_data = {
1285                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1286                for ins_id, data in raw_render_data["data"].items()
1287            }
1288
1289        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1290        if raw_stats := payload.get("stats"):
1291            builder: collections.Mapping[int, items.ItemStatsView] = {}
1292            for ins_id, stat in raw_stats["data"].items():
1293                for _, items_ in stat.items():
1294                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1295            stats = builder
1296
1297        sockets: typing.Optional[
1298            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1299        ] = None
1300        if raw_sockets := payload.get("sockets"):
1301            sockets = {
1302                int(ins_id): [
1303                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1304                ]
1305                for ins_id, item in raw_sockets["data"].items()
1306            }
1307
1308        objectives: typing.Optional[
1309            collections.Mapping[int, collections.Sequence[records.Objective]]
1310        ] = None
1311        if raw_objectives := payload.get("objectives"):
1312            objectives = {
1313                int(ins_id): [self.deserialize_objectives(objective)]
1314                for ins_id, data in raw_objectives["data"].items()
1315                for objective in data["objectives"]
1316            }
1317
1318        perks: typing.Optional[
1319            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1320        ] = None
1321        if raw_perks := payload.get("perks"):
1322            perks = {
1323                int(ins_id): [
1324                    self.deserialize_item_perk(perk) for perk in item["perks"]
1325                ]
1326                for ins_id, item in raw_perks["data"].items()
1327            }
1328
1329        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1330        if raw_plug_states := payload.get("plugStates"):
1331            pending_states: list[items.PlugItemState] = []
1332            for _, plug in raw_plug_states["data"].items():
1333                pending_states.append(self.deserialize_plug_item_state(plug))
1334            plug_states = pending_states
1335
1336        reusable_plugs: typing.Optional[
1337            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1338        ] = None
1339        if raw_re_plugs := payload.get("reusablePlugs"):
1340            reusable_plugs = {
1341                int(ins_id): [
1342                    self.deserialize_plug_item_state(state) for state in inner
1343                ]
1344                for ins_id, plug in raw_re_plugs["data"].items()
1345                for inner in list(plug["plugs"].values())
1346            }
1347
1348        plug_objectives: typing.Optional[
1349            collections.Mapping[
1350                int, collections.Mapping[int, collections.Collection[records.Objective]]
1351            ]
1352        ] = None
1353        if raw_plug_objectives := payload.get("plugObjectives"):
1354            plug_objectives = {
1355                int(ins_id): {
1356                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1357                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1358                }
1359                for ins_id, inner in raw_plug_objectives["data"].items()
1360            }
1361
1362        return components.ItemsComponent(
1363            sockets=sockets,
1364            stats=stats,
1365            render_data=render_data,
1366            instances=instances,
1367            objectives=objectives,
1368            perks=perks,
1369            plug_states=plug_states,
1370            reusable_plugs=reusable_plugs,
1371            plug_objectives=plug_objectives,
1372        )
1373
1374    def deserialize_character_component(  # type: ignore[call-arg]
1375        self, payload: typedefs.JSONObject
1376    ) -> components.CharacterComponent:
1377        character_: typing.Optional[character.Character] = None
1378        if raw_singular_character := payload.get("character"):
1379            character_ = self.deserialize_character(raw_singular_character["data"])
1380
1381        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1382        if raw_inventory := payload.get("inventory"):
1383            if "data" in raw_inventory:
1384                inventory = self.deserialize_profile_items(raw_inventory["data"])
1385
1386        activities: typing.Optional[activity.CharacterActivity] = None
1387        if raw_activities := payload.get("activities"):
1388            activities = self.deserialize_character_activity(raw_activities["data"])
1389
1390        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1391        if raw_equipments := payload.get("equipment"):
1392            equipment = self.deserialize_profile_items(raw_equipments["data"])
1393
1394        progressions_: typing.Optional[character.CharacterProgression] = None
1395        if raw_progressions := payload.get("progressions"):
1396            progressions_ = self.deserialize_character_progressions(
1397                raw_progressions["data"]
1398            )
1399
1400        render_data: typing.Optional[character.RenderedData] = None
1401        if raw_render_data := payload.get("renderData"):
1402            render_data = self.deserialize_character_render_data(
1403                raw_render_data["data"]
1404            )
1405
1406        character_records: typing.Optional[
1407            collections.Mapping[int, records.CharacterRecord]
1408        ] = None
1409        if raw_char_records := payload.get("records"):
1410            character_records = self.deserialize_characters_records(
1411                raw_char_records["data"]
1412            )
1413
1414        item_components: typing.Optional[components.ItemsComponent] = None
1415        if raw_item_components := payload.get("itemComponents"):
1416            item_components = self.deserialize_items_component(raw_item_components)
1417
1418        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1419        if raw_nodes := payload.get("presentationNodes"):
1420            nodes = {
1421                int(node_hash): self._deserialize_node(node)
1422                for node_hash, node in raw_nodes["data"]["nodes"].items()
1423            }
1424
1425        collectibles: typing.Optional[items.Collectible] = None
1426        if raw_collectibles := payload.get("collectibles"):
1427            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1428
1429        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1430        if raw_currencies := payload.get("currencyLookups"):
1431            if "data" in raw_currencies:
1432                currency_lookups = self._deserialize_currencies(raw_currencies)
1433
1434        return components.CharacterComponent(
1435            activities=activities,
1436            equipment=equipment,
1437            inventory=inventory,
1438            progressions=progressions_,
1439            render_data=render_data,
1440            character=character_,
1441            character_records=character_records,
1442            profile_records=None,
1443            item_components=item_components,
1444            currency_lookups=currency_lookups,
1445            collectibles=collectibles,
1446            nodes=nodes,
1447        )
1448
1449    def _set_entity_attrs(
1450        self, payload: typedefs.JSONObject, *, key: str = "displayProperties"
1451    ) -> entity.Entity:
1452        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1453        description: undefined.UndefinedOr[str] = undefined.UNDEFINED
1454
1455        if properties := payload[key]:
1456            if (raw_name := properties["name"]) is not typedefs.Unknown:
1457                name = raw_name
1458
1459            if (
1460                raw_description := properties["description"]
1461            ) and not typedefs.is_unknown(raw_description):
1462                description = raw_description
1463
1464        return entity.Entity(
1465            net=self._net,
1466            hash=payload["hash"],
1467            index=payload["index"],
1468            name=name,
1469            description=description,
1470            has_icon=properties["hasIcon"],
1471            icon=assets.Image(properties["icon"] if "icon" in properties else None),
1472        )
1473
1474    def deserialize_inventory_results(
1475        self, payload: typedefs.JSONObject
1476    ) -> iterators.Iterator[entity.SearchableEntity]:
1477        suggested_words: list[str] = payload["suggestedWords"]
1478
1479        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1480            return s if not typedefs.is_unknown(s) else undefined.UNDEFINED
1481
1482        return iterators.Iterator(
1483            [
1484                entity.SearchableEntity(
1485                    net=self._net,
1486                    hash=data["hash"],
1487                    entity_type=data["entityType"],
1488                    weight=data["weight"],
1489                    suggested_words=suggested_words,
1490                    name=data["displayProperties"]["name"],
1491                    has_icon=data["displayProperties"]["hasIcon"],
1492                    description=_check_unknown(
1493                        data["displayProperties"]["description"]
1494                    ),
1495                    icon=assets.Image(data["displayProperties"]["icon"]),
1496                )
1497                for data in payload["results"]["results"]
1498            ]
1499        )
1500
1501    def _deserialize_inventory_item_objects(
1502        self, payload: typedefs.JSONObject
1503    ) -> entity.InventoryEntityObjects:
1504        return entity.InventoryEntityObjects(
1505            action=payload.get("action"),
1506            set_data=payload.get("setData"),
1507            stats=payload.get("stats"),
1508            equipping_block=payload.get("equippingBlock"),
1509            translation_block=payload.get("translationBlock"),
1510            preview=payload.get("preview"),
1511            quality=payload.get("quality"),
1512            value=payload.get("value"),
1513            source_data=payload.get("sourceData"),
1514            objectives=payload.get("objectives"),
1515            plug=payload.get("plug"),
1516            metrics=payload.get("metrics"),
1517            gearset=payload.get("gearset"),
1518            sack=payload.get("sack"),
1519            sockets=payload.get("sockets"),
1520            summary=payload.get("summary"),
1521            talent_gird=payload.get("talentGrid"),
1522            investments_stats=payload.get("investmentStats"),
1523            perks=payload.get("perks"),
1524            animations=payload.get("animations", []),
1525            links=payload.get("links", []),
1526        )
1527
1528    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1529        self, payload: typedefs.JSONObject, /
1530    ) -> entity.InventoryEntity:
1531        props = self._set_entity_attrs(payload)
1532        objects = self._deserialize_inventory_item_objects(payload)
1533
1534        collectible_hash: typing.Optional[int] = None
1535        if raw_collectible_hash := payload.get("collectibleHash"):
1536            collectible_hash = int(raw_collectible_hash)
1537
1538        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1539        if raw_second_icon := payload.get("secondaryIcon"):
1540            secondary_icon = assets.Image(raw_second_icon)
1541
1542        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1543        if raw_second_overlay := payload.get("secondaryOverlay"):
1544            secondary_overlay = assets.Image(raw_second_overlay)
1545
1546        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1547        if raw_second_special := payload.get("secondarySpecial"):
1548            secondary_special = assets.Image(raw_second_special)
1549
1550        screenshot: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1551        if raw_screenshot := payload.get("screenshot"):
1552            screenshot = assets.Image(raw_screenshot)
1553
1554        watermark_icon: typing.Optional[assets.Image] = None
1555        if raw_watermark_icon := payload.get("iconWatermark"):
1556            watermark_icon = assets.Image(raw_watermark_icon)
1557
1558        watermark_shelved: typing.Optional[assets.Image] = None
1559        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1560            watermark_shelved = assets.Image(raw_watermark_shelved)
1561
1562        about: undefined.UndefinedOr[str] = undefined.UNDEFINED
1563        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1564            raw_about
1565        ):
1566            about = raw_about
1567
1568        ui_item_style: undefined.UndefinedOr[str] = undefined.UNDEFINED
1569        if (
1570            raw_ui_style := payload.get("uiItemDisplayStyle")
1571        ) and not typedefs.is_unknown(raw_ui_style):
1572            ui_item_style = raw_ui_style
1573
1574        tier_and_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1575        if (
1576            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1577        ) and not typedefs.is_unknown(raw_tier_and_name):
1578            tier_and_name = raw_tier_and_name
1579
1580        type_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1581        if (
1582            raw_type_name := payload.get("itemTypeDisplayName")
1583        ) and not typedefs.is_unknown(raw_type_name):
1584            type_name = raw_type_name
1585
1586        display_source: undefined.UndefinedOr[str] = undefined.UNDEFINED
1587        if (
1588            raw_display_source := payload.get("displaySource")
1589        ) and not typedefs.is_unknown(raw_display_source):
1590            display_source = raw_display_source
1591
1592        lorehash: typing.Optional[int] = None
1593        if raw_lore_hash := payload.get("loreHash"):
1594            lorehash = int(raw_lore_hash)
1595
1596        summary_hash: typing.Optional[int] = None
1597        if raw_summary_hash := payload.get("summaryItemHash"):
1598            summary_hash = raw_summary_hash
1599
1600        breaker_type_hash: typing.Optional[int] = None
1601        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1602            breaker_type_hash = int(raw_breaker_type_hash)
1603
1604        damage_types: typing.Optional[collections.Sequence[int]] = None
1605        if raw_damage_types := payload.get("damageTypes"):
1606            damage_types = [int(type_) for type_ in raw_damage_types]
1607
1608        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1609        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1610            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1611
1612        default_damagetype_hash: typing.Optional[int] = None
1613        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1614            default_damagetype_hash = int(raw_defaultdmg_hash)
1615
1616        emblem_objective_hash: typing.Optional[int] = None
1617        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1618            emblem_objective_hash = int(raw_emblem_obj_hash)
1619
1620        tier_type: typing.Optional[enums.TierType] = None
1621        tier: typing.Optional[enums.ItemTier] = None
1622        bucket_hash: typing.Optional[int] = None
1623        recovery_hash: typing.Optional[int] = None
1624        tier_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1625        isinstance_item: bool = False
1626        expire_tool_tip: undefined.UndefinedOr[str] = undefined.UNDEFINED
1627        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.UNDEFINED
1628        suppress_expiration: bool = False
1629        max_stack_size: typing.Optional[int] = None
1630        stack_label: undefined.UndefinedOr[str] = undefined.UNDEFINED
1631
1632        if inventory := payload.get("inventory"):
1633            tier_type = enums.TierType(int(inventory["tierType"]))
1634            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1635            bucket_hash = int(inventory["bucketTypeHash"])
1636            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1637            tier_name = inventory["tierTypeName"]
1638            isinstance_item = inventory["isInstanceItem"]
1639            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1640            max_stack_size = int(inventory["maxStackSize"])
1641
1642            try:
1643                stack_label = inventory["stackUniqueLabel"]
1644            except KeyError:
1645                pass
1646
1647        return entity.InventoryEntity(
1648            net=self._net,
1649            collectible_hash=collectible_hash,
1650            name=props.name,
1651            about=about,
1652            emblem_objective_hash=emblem_objective_hash,
1653            suppress_expiration=suppress_expiration,
1654            max_stack_size=max_stack_size,
1655            stack_label=stack_label,
1656            tier=tier,
1657            tier_type=tier_type,
1658            tier_name=tier_name,
1659            bucket_hash=bucket_hash,
1660            recovery_bucket_hash=recovery_hash,
1661            isinstance_item=isinstance_item,
1662            expire_in_orbit_message=expire_in_orbit_message,
1663            expiration_tooltip=expire_tool_tip,
1664            lore_hash=lorehash,
1665            type_and_tier_name=tier_and_name,
1666            summary_hash=summary_hash,
1667            ui_display_style=ui_item_style,
1668            type_name=type_name,
1669            breaker_type_hash=breaker_type_hash,
1670            description=props.description,
1671            display_source=display_source,
1672            hash=props.hash,
1673            damage_types=damage_types,
1674            index=props.index,
1675            icon=props.icon,
1676            has_icon=props.has_icon,
1677            screenshot=screenshot,
1678            watermark_icon=watermark_icon,
1679            watermark_shelved=watermark_shelved,
1680            secondary_icon=secondary_icon,
1681            secondary_overlay=secondary_overlay,
1682            secondary_special=secondary_special,
1683            type=enums.ItemType(int(payload["itemType"])),
1684            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1685            trait_ids=[trait for trait in payload.get("traitIds", [])],
1686            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1687            item_class=enums.Class(int(payload["classType"])),
1688            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1689            breaker_type=int(payload["breakerType"]),
1690            default_damagetype=int(payload["defaultDamageType"]),
1691            default_damagetype_hash=default_damagetype_hash,
1692            damagetype_hashes=damagetype_hashes,
1693            tooltip_notifications=payload["tooltipNotifications"],
1694            not_transferable=payload["nonTransferrable"],
1695            allow_actions=payload["allowActions"],
1696            is_equippable=payload["equippable"],
1697            objects=objects,
1698            background_colors=payload.get("backgroundColor", {}),
1699            season_hash=payload.get("seasonHash"),
1700            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1701        )
1702
1703    def deserialize_objective_entity(
1704        self, payload: typedefs.JSONObject, /
1705    ) -> entity.ObjectiveEntity:
1706        props = self._set_entity_attrs(payload)
1707        return entity.ObjectiveEntity(
1708            net=self._net,
1709            hash=props.hash,
1710            index=props.index,
1711            description=props.description,
1712            name=props.name,
1713            has_icon=props.has_icon,
1714            icon=props.icon,
1715            unlock_value_hash=payload["unlockValueHash"],
1716            completion_value=payload["completionValue"],
1717            scope=entity.GatingScope(int(payload["scope"])),
1718            location_hash=payload["locationHash"],
1719            allowed_negative_value=payload["allowNegativeValue"],
1720            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1721            counting_downward=payload["isCountingDownward"],
1722            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1723            progress_description=payload["progressDescription"],
1724            perks=payload["perks"],
1725            stats=payload["stats"],
1726            minimum_visibility=payload["minimumVisibilityThreshold"],
1727            allow_over_completion=payload["allowOvercompletion"],
1728            show_value_style=payload["showValueOnComplete"],
1729            display_only_objective=payload["isDisplayOnlyObjective"],
1730            complete_value_style=entity.ValueUIStyle(
1731                int(payload["completedValueStyle"])
1732            ),
1733            progress_value_style=entity.ValueUIStyle(
1734                int(payload["inProgressValueStyle"])
1735            ),
1736            ui_label=payload["uiLabel"],
1737            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1738        )
1739
1740    def _deserialize_activity_values(
1741        self, payload: typedefs.JSONObject, /
1742    ) -> activity.ActivityValues:
1743        team: typing.Optional[int] = None
1744        if raw_team := payload.get("team"):
1745            team = raw_team["basic"]["value"]
1746        return activity.ActivityValues(
1747            assists=payload["assists"]["basic"]["value"],
1748            deaths=payload["deaths"]["basic"]["value"],
1749            kills=payload["kills"]["basic"]["value"],
1750            is_completed=bool(payload["completed"]["basic"]["value"]),
1751            opponents_defeated=payload["opponentsDefeated"]["basic"]["value"],
1752            efficiency=payload["efficiency"]["basic"]["value"],
1753            kd_ratio=payload["killsDeathsRatio"]["basic"]["value"],
1754            kd_assists=payload["killsDeathsAssists"]["basic"]["value"],
1755            score=payload["score"]["basic"]["value"],
1756            duration=payload["activityDurationSeconds"]["basic"]["displayValue"],
1757            team=team,
1758            completion_reason=payload["completionReason"]["basic"]["displayValue"],
1759            fireteam_id=payload["fireteamId"]["basic"]["value"],
1760            start_seconds=payload["startSeconds"]["basic"]["value"],
1761            played_time=payload["timePlayedSeconds"]["basic"]["displayValue"],
1762            player_count=payload["playerCount"]["basic"]["value"],
1763            team_score=payload["teamScore"]["basic"]["value"],
1764        )
1765
1766    def deserialize_activity(
1767        self,
1768        payload: typedefs.JSONObject,
1769        /,
1770    ) -> activity.Activity:
1771        period = time.clean_date(payload["period"])
1772        details = payload["activityDetails"]
1773        ref_id = int(details["referenceId"])
1774        instance_id = int(details["instanceId"])
1775        mode = enums.GameMode(details["mode"])
1776        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1777        is_private = details["isPrivate"]
1778        membership_type = enums.MembershipType(int(details["membershipType"]))
1779
1780        # Since we're using the same fields for post activity method
1781        # this check is required since post activity doesn't values values
1782        values = self._deserialize_activity_values(payload["values"])
1783
1784        return activity.Activity(
1785            net=self._net,
1786            hash=ref_id,
1787            instance_id=instance_id,
1788            mode=mode,
1789            modes=modes,
1790            is_private=is_private,
1791            membership_type=membership_type,
1792            occurred_at=period,
1793            values=values,
1794        )
1795
1796    def deserialize_activities(
1797        self, payload: typedefs.JSONObject
1798    ) -> iterators.Iterator[activity.Activity]:
1799        return iterators.Iterator(
1800            [
1801                self.deserialize_activity(activity_)
1802                for activity_ in payload["activities"]
1803            ]
1804        )
1805
1806    def deserialize_extended_weapon_values(
1807        self, payload: typedefs.JSONObject
1808    ) -> activity.ExtendedWeaponValues:
1809        assists: typing.Optional[int] = None
1810        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1811            assists = raw_assists["basic"]["value"]
1812        assists_damage: typing.Optional[int] = None
1813
1814        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1815            assists_damage = raw_assists_damage["basic"]["value"]
1816
1817        return activity.ExtendedWeaponValues(
1818            reference_id=int(payload["referenceId"]),
1819            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1820            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1821                "value"
1822            ],
1823            assists=assists,
1824            assists_damage=assists_damage,
1825            precision_kills_percentage=(
1826                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1827                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1828                    "displayValue"
1829                ],
1830            ),
1831        )
1832
1833    def _deserialize_extended_values(
1834        self, payload: typedefs.JSONObject
1835    ) -> activity.ExtendedValues:
1836        weapons: typing.Optional[
1837            collections.Collection[activity.ExtendedWeaponValues]
1838        ] = None
1839
1840        if raw_weapons := payload.get("weapons"):
1841            weapons = [
1842                self.deserialize_extended_weapon_values(value) for value in raw_weapons
1843            ]
1844
1845        return activity.ExtendedValues(
1846            precision_kills=payload["values"]["precisionKills"]["basic"]["value"],
1847            grenade_kills=payload["values"]["weaponKillsGrenade"]["basic"]["value"],
1848            melee_kills=payload["values"]["weaponKillsMelee"]["basic"]["value"],
1849            super_kills=payload["values"]["weaponKillsSuper"]["basic"]["value"],
1850            ability_kills=payload["values"]["weaponKillsAbility"]["basic"]["value"],
1851            weapons=weapons,
1852        )
1853
1854    def deserialize_post_activity_player(
1855        self, payload: typedefs.JSONObject, /
1856    ) -> activity.PostActivityPlayer:
1857        player = payload["player"]
1858
1859        class_hash: typedefs.NoneOr[int] = None
1860        if (class_hash := player.get("classHash")) is not None:
1861            class_hash = class_hash
1862
1863        race_hash: typedefs.NoneOr[int] = None
1864        if (race_hash := player.get("raceHash")) is not None:
1865            race_hash = race_hash
1866
1867        gender_hash: typedefs.NoneOr[int] = None
1868        if (gender_hash := player.get("genderHash")) is not None:
1869            gender_hash = gender_hash
1870
1871        character_class: undefined.UndefinedOr[str] = undefined.UNDEFINED
1872        if (
1873            character_class := player.get("characterClass")
1874        ) and not typedefs.is_unknown(character_class):
1875            character_class = character_class
1876
1877        character_level: typedefs.NoneOr[int] = None
1878        if (character_level := player.get("characterLevel")) is not None:
1879            character_level = character_level
1880
1881        return activity.PostActivityPlayer(
1882            standing=int(payload["standing"]),
1883            score=int(payload["score"]["basic"]["value"]),
1884            character_id=payload["characterId"],
1885            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1886            character_class=character_class,
1887            character_level=character_level,
1888            race_hash=race_hash,
1889            gender_hash=gender_hash,
1890            class_hash=class_hash,
1891            light_level=int(player["lightLevel"]),
1892            emblem_hash=int(player["emblemHash"]),
1893            values=self._deserialize_activity_values(payload["values"]),
1894            extended_values=self._deserialize_extended_values(payload["extended"]),
1895        )
1896
1897    def _deserialize_post_activity_team(
1898        self, payload: typedefs.JSONObject
1899    ) -> activity.PostActivityTeam:
1900        return activity.PostActivityTeam(
1901            id=payload["teamId"],
1902            is_defeated=bool(payload["standing"]["basic"]["value"]),
1903            score=int(payload["score"]["basic"]["value"]),
1904            name=payload["teamName"],
1905        )
1906
1907    def deserialize_post_activity(
1908        self, payload: typedefs.JSONObject
1909    ) -> activity.PostActivity:
1910        period = time.clean_date(payload["period"])
1911        details = payload["activityDetails"]
1912        ref_id = int(details["referenceId"])
1913        instance_id = int(details["instanceId"])
1914        mode = enums.GameMode(details["mode"])
1915        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1916        is_private = details["isPrivate"]
1917        membership_type = enums.MembershipType(int(details["membershipType"]))
1918        return activity.PostActivity(
1919            net=self._net,
1920            hash=ref_id,
1921            membership_type=membership_type,
1922            instance_id=instance_id,
1923            mode=mode,
1924            modes=modes,
1925            is_private=is_private,
1926            occurred_at=period,
1927            starting_phase=int(payload["startingPhaseIndex"]),
1928            players=[
1929                self.deserialize_post_activity_player(player)
1930                for player in payload["entries"]
1931            ],
1932            teams=[
1933                self._deserialize_post_activity_team(team) for team in payload["teams"]
1934            ],
1935        )
1936
1937    def _deserialize_aggregated_activity_values(
1938        self, payload: typedefs.JSONObject
1939    ) -> activity.AggregatedActivityValues:
1940        # This ID is always the same for all aggregated values.
1941        activity_id = int(payload["fastestCompletionMsForActivity"]["activityId"])
1942
1943        return activity.AggregatedActivityValues(
1944            id=activity_id,
1945            fastest_completion_time=(
1946                int(payload["fastestCompletionMsForActivity"]["basic"]["value"]),
1947                payload["fastestCompletionMsForActivity"]["basic"]["displayValue"],
1948            ),
1949            completions=int(payload["activityCompletions"]["basic"]["value"]),
1950            kills=int(payload["activityKills"]["basic"]["value"]),
1951            deaths=int(payload["activityDeaths"]["basic"]["value"]),
1952            assists=int(payload["activityAssists"]["basic"]["value"]),
1953            seconds_played=(
1954                int(payload["activitySecondsPlayed"]["basic"]["value"]),
1955                payload["activitySecondsPlayed"]["basic"]["displayValue"],
1956            ),
1957            wins=int(payload["activityWins"]["basic"]["value"]),
1958            goals_missed=int(payload["activityGoalsMissed"]["basic"]["value"]),
1959            special_actions=int(payload["activitySpecialActions"]["basic"]["value"]),
1960            best_goals_hit=int(payload["activityBestGoalsHit"]["basic"]["value"]),
1961            best_single_score=int(
1962                payload["activityBestSingleGameScore"]["basic"]["value"]
1963            ),
1964            goals_hit=int(payload["activityGoalsHit"]["basic"]["value"]),
1965            special_score=int(payload["activitySpecialScore"]["basic"]["value"]),
1966            kd_assists=int(payload["activityKillsDeathsAssists"]["basic"]["value"]),
1967            kd_ratio=float(
1968                payload["activityKillsDeathsAssists"]["basic"]["displayValue"]
1969            ),
1970            precision_kills=int(payload["activityPrecisionKills"]["basic"]["value"]),
1971        )
1972
1973    def deserialize_aggregated_activity(
1974        self, payload: typedefs.JSONObject
1975    ) -> activity.AggregatedActivity:
1976        return activity.AggregatedActivity(
1977            hash=int(payload["activityHash"]),
1978            values=self._deserialize_aggregated_activity_values(payload["values"]),
1979        )
1980
1981    def deserialize_aggregated_activities(
1982        self, payload: typedefs.JSONObject
1983    ) -> iterators.Iterator[activity.AggregatedActivity]:
1984        return iterators.Iterator(
1985            [
1986                self.deserialize_aggregated_activity(activity)
1987                for activity in payload["activities"]
1988            ]
1989        )
1990
1991    def deserialize_linked_profiles(
1992        self, payload: typedefs.JSONObject
1993    ) -> profile.LinkedProfile:
1994        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
1995        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
1996        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
1997
1998        if raw_profile := payload.get("profiles"):
1999            for pfile in raw_profile:
2000                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2001
2002        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2003            for raw_error_pfile in raw_profiles_with_errors:
2004                if error_pfile := raw_error_pfile.get("infoCard"):
2005                    error_profiles_vec.append(
2006                        self.deserialize_destiny_membership(error_pfile)
2007                    )
2008
2009        return profile.LinkedProfile(
2010            bungie=bungie_user,
2011            profiles=profiles_vec,
2012            profiles_with_errors=error_profiles_vec,
2013        )
2014
2015    def deserialize_clan_banners(
2016        self, payload: typedefs.JSONObject
2017    ) -> collections.Sequence[clans.ClanBanner]:
2018        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2019        if banners := payload.get("clanBannerDecals"):
2020            for k, v in banners.items():
2021                banner_obj = clans.ClanBanner(
2022                    id=int(k),
2023                    foreground=assets.Image(v["foregroundPath"]),
2024                    background=assets.Image(v["backgroundPath"]),
2025                )
2026                banners_seq.append(banner_obj)
2027        return banners_seq
2028
2029    def deserialize_public_milestone_content(
2030        self, payload: typedefs.JSONObject
2031    ) -> milestones.MilestoneContent:
2032        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2033        if raw_categories := payload.get("itemCategories"):
2034            for item in raw_categories:
2035                title = undefined.UNDEFINED
2036                if raw_title := item.get("title"):
2037                    if raw_title != typedefs.Unknown:
2038                        title = raw_title
2039                if raw_hashes := item.get("itemHashes"):
2040                    hashes: collections.Sequence[int] = raw_hashes
2041
2042                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2043
2044        about = undefined.UNDEFINED
2045        if (raw_about := payload["about"]) != typedefs.Unknown:
2046            about = raw_about
2047
2048        status = undefined.UNDEFINED
2049        if (raw_status := payload["status"]) != typedefs.Unknown:
2050            status = raw_status
2051
2052        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2053        if raw_tips := payload.get("tips"):
2054            for raw_tip in raw_tips:
2055                if raw_tip == typedefs.Unknown:
2056                    raw_tip = undefined.UNDEFINED
2057                tips.append(raw_tip)
2058
2059        return milestones.MilestoneContent(
2060            about=about, status=status, tips=tips, items=items_categoris
2061        )
2062
2063    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2064        name = undefined.UNDEFINED
2065        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2066            name = raw_name
2067
2068        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2069
2070        if raw_bungie_user := payload.get("bungieNetUser"):
2071            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2072
2073        return friends.Friend(
2074            net=self._net,
2075            id=int(payload["lastSeenAsMembershipId"]),
2076            name=name,
2077            code=payload.get("bungieGlobalDisplayNameCode"),
2078            relationship=enums.Relationship(payload["relationship"]),
2079            user=bungie_user,
2080            online_status=enums.Presence(payload["onlineStatus"]),
2081            online_title=payload["onlineTitle"],
2082            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2083        )
2084
2085    def deserialize_friends(
2086        self, payload: typedefs.JSONObject
2087    ) -> collections.Sequence[friends.Friend]:
2088        mut_seq: typing.MutableSequence[friends.Friend] = []
2089        if raw_friends := payload.get("friends"):
2090            for friend in raw_friends:
2091                mut_seq.append(self.deserialize_friend(friend))
2092        return mut_seq
2093
2094    def deserialize_friend_requests(
2095        self, payload: typedefs.JSONObject
2096    ) -> friends.FriendRequestView:
2097        incoming: typing.MutableSequence[friends.Friend] = []
2098        outgoing: typing.MutableSequence[friends.Friend] = []
2099
2100        if raw_incoming_requests := payload.get("incomingRequests"):
2101            for incoming_request in raw_incoming_requests:
2102                incoming.append(self.deserialize_friend(incoming_request))
2103
2104        if raw_outgoing_requests := payload.get("outgoingRequests"):
2105            for outgoing_request in raw_outgoing_requests:
2106                outgoing.append(self.deserialize_friend(outgoing_request))
2107
2108        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)
2109
2110    def _set_fireteam_fields(
2111        self, payload: typedefs.JSONObject, total_results: typing.Optional[int] = None
2112    ) -> fireteams.Fireteam:
2113        activity_type = fireteams.FireteamActivity(payload["activityType"])
2114        return fireteams.Fireteam(
2115            id=int(payload["fireteamId"]),
2116            group_id=int(payload["groupId"]),
2117            platform=fireteams.FireteamPlatform(payload["platform"]),
2118            is_immediate=payload["isImmediate"],
2119            activity_type=activity_type,
2120            owner_id=int(payload["ownerMembershipId"]),
2121            player_slot_count=payload["playerSlotCount"],
2122            available_player_slots=payload["availablePlayerSlotCount"],
2123            available_alternate_slots=payload["availableAlternateSlotCount"],
2124            title=payload["title"],
2125            date_created=time.clean_date(payload["dateCreated"]),
2126            is_public=payload["isPublic"],
2127            locale=fireteams.FireteamLanguage(payload["locale"]),
2128            is_valid=payload["isValid"],
2129            last_modified=time.clean_date(payload["datePlayerModified"]),
2130            total_results=total_results or 0,
2131        )
2132
2133    def deserialize_fireteams(
2134        self, payload: typedefs.JSONObject
2135    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2136        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2137
2138        result: list[typedefs.JSONObject]
2139        if not (result := payload["results"]):
2140            return None
2141        for elem in result:
2142            fireteams_.append(
2143                self._set_fireteam_fields(
2144                    elem, total_results=int(payload["totalResults"])
2145                )
2146            )
2147        return fireteams_
2148
2149    def deserialize_fireteam_destiny_users(
2150        self, payload: typedefs.JSONObject
2151    ) -> fireteams.FireteamUser:
2152        destiny_obj = self.deserialize_destiny_membership(payload)
2153        # We could helpers.just return a DestinyMembership object but this is
2154        # missing the fireteam display name and id fields.
2155        return fireteams.FireteamUser(
2156            net=self._net,
2157            id=destiny_obj.id,
2158            code=destiny_obj.code,
2159            icon=destiny_obj.icon,
2160            types=destiny_obj.types,
2161            type=destiny_obj.type,
2162            is_public=destiny_obj.is_public,
2163            crossave_override=destiny_obj.crossave_override,
2164            name=destiny_obj.name,
2165            last_seen_name=destiny_obj.last_seen_name,
2166            fireteam_display_name=payload["FireteamDisplayName"],
2167            fireteam_membership_id=enums.MembershipType(
2168                payload["FireteamMembershipType"]
2169            ),
2170        )
2171
2172    def deserialize_fireteam_members(
2173        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2174    ) -> collections.Sequence[fireteams.FireteamMember]:
2175        members_: list[fireteams.FireteamMember] = []
2176        if members := payload.get("Members" if not alternatives else "Alternates"):
2177            for member in members:
2178                bungie_fields = self.deserialize_partial_bungie_user(member)
2179                members_fields = fireteams.FireteamMember(
2180                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2181                    has_microphone=member["hasMicrophone"],
2182                    character_id=int(member["characterId"]),
2183                    date_joined=time.clean_date(member["dateJoined"]),
2184                    last_platform_invite_date=time.clean_date(
2185                        member["lastPlatformInviteAttemptDate"]
2186                    ),
2187                    last_platform_invite_result=int(
2188                        member["lastPlatformInviteAttemptResult"]
2189                    ),
2190                    net=self._net,
2191                    name=bungie_fields.name,
2192                    id=bungie_fields.id,
2193                    icon=bungie_fields.icon,
2194                    is_public=bungie_fields.is_public,
2195                    crossave_override=bungie_fields.crossave_override,
2196                    types=bungie_fields.types,
2197                    type=bungie_fields.type,
2198                )
2199                members_.append(members_fields)
2200        return members_
2201
2202    # TODO: Don't return union here. In general fix this method.
2203    def deserialize_available_fireteams(
2204        self,
2205        data: typedefs.JSONObject,
2206        *,
2207        no_results: bool = False,
2208    ) -> typing.Union[
2209        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2210    ]:
2211        fireteams_: list[fireteams.AvailableFireteam] = []
2212
2213        # This needs to be used outside the results
2214        # JSON key.
2215        if no_results:
2216            payload = data.copy()
2217
2218        if (results := payload.get("results")) is not None:
2219            for fireteam in results:
2220                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2221                fireteams_fields = fireteams.AvailableFireteam(
2222                    id=found_fireteams.id,
2223                    group_id=found_fireteams.group_id,
2224                    platform=found_fireteams.platform,
2225                    activity_type=found_fireteams.activity_type,
2226                    is_immediate=found_fireteams.is_immediate,
2227                    is_public=found_fireteams.is_public,
2228                    is_valid=found_fireteams.is_valid,
2229                    owner_id=found_fireteams.owner_id,
2230                    player_slot_count=found_fireteams.player_slot_count,
2231                    available_player_slots=found_fireteams.available_player_slots,
2232                    available_alternate_slots=found_fireteams.available_alternate_slots,
2233                    title=found_fireteams.title,
2234                    date_created=found_fireteams.date_created,
2235                    locale=found_fireteams.locale,
2236                    last_modified=found_fireteams.last_modified,
2237                    total_results=found_fireteams.total_results,
2238                    members=self.deserialize_fireteam_members(payload),
2239                    alternatives=self.deserialize_fireteam_members(
2240                        payload, alternatives=True
2241                    ),
2242                )
2243            if no_results:
2244                return fireteams_fields
2245            else:
2246                fireteams_.append(fireteams_fields)
2247        return fireteams_
2248
2249    def deserialize_fireteam_party(
2250        self, payload: typedefs.JSONObject
2251    ) -> fireteams.FireteamParty:
2252        last_destination_hash: typing.Optional[int] = None
2253        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2254            last_destination_hash = int(raw_dest_hash)
2255
2256        return fireteams.FireteamParty(
2257            members=[
2258                self._deserialize_fireteam_party_member(member)
2259                for member in payload["partyMembers"]
2260            ],
2261            activity=self._deserialize_fireteam_party_current_activity(
2262                payload["currentActivity"]
2263            ),
2264            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2265            last_destination_hash=last_destination_hash,
2266            tracking=payload["tracking"],
2267        )
2268
2269    def _deserialize_fireteam_party_member(
2270        self, payload: typedefs.JSONObject
2271    ) -> fireteams.FireteamPartyMember:
2272        status = fireteams.FireteamPartyMemberState(payload["status"])
2273        displayname: undefined.UndefinedOr[str] = undefined.UNDEFINED
2274        if raw_name := payload.get("displayName"):
2275            displayname = raw_name
2276
2277        return fireteams.FireteamPartyMember(
2278            membership_id=int(payload["membershipId"]),
2279            emblem_hash=int(payload["emblemHash"]),
2280            status=status,
2281            display_name=displayname,
2282        )
2283
2284    def _deserialize_fireteam_party_current_activity(
2285        self, payload: typedefs.JSONObject
2286    ) -> fireteams.FireteamPartyCurrentActivity:
2287        start_date: typing.Optional[datetime.datetime] = None
2288        if raw_start_date := payload.get("startTime"):
2289            start_date = time.clean_date(raw_start_date)
2290
2291        end_date: typing.Optional[datetime.datetime] = None
2292        if raw_end_date := payload.get("endTime"):
2293            end_date = time.clean_date(raw_end_date)
2294        return fireteams.FireteamPartyCurrentActivity(
2295            start_time=start_date,
2296            end_time=end_date,
2297            score=float(payload["score"]),
2298            highest_opposing_score=float(payload["highestOpposingFactionScore"]),
2299            opponenst_count=int(payload["numberOfOpponents"]),
2300            player_count=int(payload["numberOfPlayers"]),
2301        )
2302
2303    def _deserialize_fireteam_party_settings(
2304        self, payload: typedefs.JSONObject
2305    ) -> fireteams.FireteamPartySettings:
2306        closed_reasons = enums.ClosedReasons(payload["closedReasons"])
2307        return fireteams.FireteamPartySettings(
2308            open_slots=int(payload["openSlots"]),
2309            privacy_setting=enums.PrivacySetting(int(payload["privacySetting"])),
2310            closed_reasons=closed_reasons,
2311        )
2312
2313    def deserialize_seasonal_artifact(
2314        self, payload: typedefs.JSONObject
2315    ) -> season.Artifact:
2316        if raw_artifact := payload.get("seasonalArtifact"):
2317            if points := raw_artifact.get("pointProgression"):
2318                points_prog = progressions.Progression(
2319                    hash=points["progressionHash"],
2320                    level=points["level"],
2321                    cap=points["levelCap"],
2322                    daily_limit=points["dailyLimit"],
2323                    weekly_limit=points["weeklyLimit"],
2324                    current_progress=points["currentProgress"],
2325                    daily_progress=points["dailyProgress"],
2326                    needed=points["progressToNextLevel"],
2327                    next_level=points["nextLevelAt"],
2328                )
2329
2330            if bonus := raw_artifact.get("powerBonusProgression"):
2331                power_bonus_prog = progressions.Progression(
2332                    hash=bonus["progressionHash"],
2333                    level=bonus["level"],
2334                    cap=bonus["levelCap"],
2335                    daily_limit=bonus["dailyLimit"],
2336                    weekly_limit=bonus["weeklyLimit"],
2337                    current_progress=bonus["currentProgress"],
2338                    daily_progress=bonus["dailyProgress"],
2339                    needed=bonus["progressToNextLevel"],
2340                    next_level=bonus["nextLevelAt"],
2341                )
2342            artifact = season.Artifact(
2343                hash=raw_artifact["artifactHash"],
2344                power_bonus=raw_artifact["powerBonus"],
2345                acquired_points=raw_artifact["pointsAcquired"],
2346                bonus=power_bonus_prog,
2347                points=points_prog,
2348            )
2349        return artifact
2350
2351    def deserialize_profile_progression(
2352        self, payload: typedefs.JSONObject
2353    ) -> profile.ProfileProgression:
2354        return profile.ProfileProgression(
2355            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2356            checklist={
2357                int(check_id): checklists
2358                for check_id, checklists in payload["data"]["checklists"].items()
2359            },
2360        )
2361
2362    def deserialize_instanced_item(
2363        self, payload: typedefs.JSONObject
2364    ) -> items.ItemInstance:
2365        damage_type_hash: typing.Optional[int] = None
2366        if raw_damagetype_hash := payload.get("damageTypeHash"):
2367            damage_type_hash = int(raw_damagetype_hash)
2368
2369        required_hashes: typing.Optional[collections.Collection[int]] = None
2370        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2371            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2372
2373        breaker_type: typing.Optional[items.ItemBreakerType] = None
2374        if raw_break_type := payload.get("breakerType"):
2375            breaker_type = items.ItemBreakerType(int(raw_break_type))
2376
2377        breaker_type_hash: typing.Optional[int] = None
2378        if raw_break_type_hash := payload.get("breakerTypeHash"):
2379            breaker_type_hash = int(raw_break_type_hash)
2380
2381        energy: typing.Optional[items.ItemEnergy] = None
2382        if raw_energy := payload.get("energy"):
2383            energy = self.deserialize_item_energy(raw_energy)
2384
2385        primary_stats = None
2386        if raw_primary_stats := payload.get("primaryStat"):
2387            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2388
2389        return items.ItemInstance(
2390            damage_type=enums.DamageType(int(payload["damageType"])),
2391            damage_type_hash=damage_type_hash,
2392            primary_stat=primary_stats,
2393            item_level=int(payload["itemLevel"]),
2394            quality=int(payload["quality"]),
2395            is_equipped=payload["isEquipped"],
2396            can_equip=payload["canEquip"],
2397            equip_required_level=int(payload["equipRequiredLevel"]),
2398            required_equip_unlock_hashes=required_hashes,
2399            cant_equip_reason=int(payload["cannotEquipReason"]),
2400            breaker_type=breaker_type,
2401            breaker_type_hash=breaker_type_hash,
2402            energy=energy,
2403        )
2404
2405    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2406        energy_hash: typing.Optional[int] = None
2407        if raw_energy_hash := payload.get("energyTypeHash"):
2408            energy_hash = int(raw_energy_hash)
2409
2410        return items.ItemEnergy(
2411            hash=energy_hash,
2412            type=items.ItemEnergyType(int(payload["energyType"])),
2413            capacity=int(payload["energyCapacity"]),
2414            used_energy=int(payload["energyUsed"]),
2415            unused_energy=int(payload["energyUnused"]),
2416        )
2417
2418    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2419        perk_hash: typing.Optional[int] = None
2420        if raw_perk_hash := payload.get("perkHash"):
2421            perk_hash = int(raw_perk_hash)
2422
2423        return items.ItemPerk(
2424            hash=perk_hash,
2425            icon=assets.Image(payload["iconPath"]),
2426            is_active=payload["isActive"],
2427            is_visible=payload["visible"],
2428        )
2429
2430    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2431        plug_hash: typing.Optional[int] = None
2432        if raw_plug_hash := payload.get("plugHash"):
2433            plug_hash = int(raw_plug_hash)
2434
2435        enable_fail_indexes: typing.Optional[list[int]] = None
2436        if raw_indexes := payload.get("enableFailIndexes"):
2437            enable_fail_indexes = [int(index) for index in raw_indexes]
2438
2439        return items.ItemSocket(
2440            plug_hash=plug_hash,
2441            is_enabled=payload["isEnabled"],
2442            enable_fail_indexes=enable_fail_indexes,
2443            is_visible=payload.get("visible"),
2444        )
2445
2446    def deserialize_item_stats_view(
2447        self, payload: typedefs.JSONObject
2448    ) -> items.ItemStatsView:
2449        return items.ItemStatsView(
2450            stat_hash=payload.get("statHash"), value=payload.get("value")
2451        )
2452
2453    def deserialize_plug_item_state(
2454        self, payload: typedefs.JSONObject
2455    ) -> items.PlugItemState:
2456        item_hash: typing.Optional[int] = None
2457        if raw_item_hash := payload.get("plugItemHash"):
2458            item_hash = int(raw_item_hash)
2459
2460        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2461        if raw_fail_indexes := payload.get("insertFailIndexes"):
2462            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2463
2464        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2465        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2466            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2467
2468        return items.PlugItemState(
2469            item_hash=item_hash,
2470            insert_fail_indexes=insert_fail_indexes,
2471            enable_fail_indexes=enable_fail_indexes,
2472            is_enabled=payload["enabled"],
2473            can_insert=payload["canInsert"],
2474        )

The base deserialization factory class for all aiobungie objects.

This entity factory is used to deserialize JSON responses from the REST client and turning them into a aiobungie.crates Python classes.

Factory(net: aiobungie.traits.Netrunner)
70    def __init__(self, net: traits.Netrunner) -> None:
71        self._net = net
def deserialize_bungie_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.BungieUser:
73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
74        return user.BungieUser(
75            id=int(data["membershipId"]),
76            created_at=time.clean_date(data["firstAccess"]),
77            name=data.get("cachedBungieGlobalDisplayName", undefined.UNDEFINED),
78            is_deleted=data["isDeleted"],
79            about=data["about"],
80            updated_at=time.clean_date(data["lastUpdate"]),
81            psn_name=data.get("psnDisplayName", None),
82            stadia_name=data.get("stadiaDisplayName", None),
83            steam_name=data.get("steamDisplayName", None),
84            twitch_name=data.get("twitchDisplayName", None),
85            blizzard_name=data.get("blizzardDisplayName", None),
86            status=data["statusText"],
87            locale=data["locale"],
88            picture=assets.Image(path=str(data["profilePicturePath"])),
89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
90            unique_name=data.get("uniqueName", None),
91            theme_id=int(data["profileTheme"]),
92            show_activity=bool(data["showActivity"]),
93            theme_name=data["profileThemeName"],
94            display_title=data["userTitleDisplay"],
95        )

Deserialize a raw JSON Bungie.net user only payload into a user object.

This only returns the Bungie.net user and not the Destiny memberships.

Parameters
Returns
def deserialize_partial_bungie_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.PartialBungieUser:
 97    def deserialize_partial_bungie_user(
 98        self, payload: typedefs.JSONObject
 99    ) -> user.PartialBungieUser:
100        return user.PartialBungieUser(
101            net=self._net,
102            types=[
103                enums.MembershipType(type_)
104                for type_ in payload.get("applicableMembershipTypes", [])
105            ],
106            name=payload.get("displayName", undefined.UNDEFINED),
107            id=int(payload["membershipId"]),
108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
109            is_public=payload["isPublic"],
110            icon=assets.Image(payload.get("iconPath", "")),
111            type=enums.MembershipType(payload["membershipType"]),
112        )

Deserialize a raw JSON of a partial bungieNetUserInfo.

A partial user is a bungie.net user payload with missing information from the main BungieUser object.

Parameters
Returns
def deserialize_destiny_membership( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.DestinyMembership:
114    def deserialize_destiny_membership(
115        self, payload: typedefs.JSONObject
116    ) -> user.DestinyMembership:
117        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
118        if (
119            raw_name := payload.get("bungieGlobalDisplayName", "")
120        ) and not typedefs.is_unknown(raw_name):
121            name = raw_name
122
123        return user.DestinyMembership(
124            net=self._net,
125            id=int(payload["membershipId"]),
126            name=name,
127            code=payload.get("bungieGlobalDisplayNameCode", None),
128            last_seen_name=payload.get("LastSeenDisplayName")
129            or payload.get("displayName")  # noqa: W503
130            or "",  # noqa: W503
131            type=enums.MembershipType(payload["membershipType"]),
132            is_public=payload["isPublic"],
133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
134            icon=assets.Image(payload.get("iconPath", "")),
135            types=[
136                enums.MembershipType(type_)
137                for type_ in payload.get("applicableMembershipTypes", [])
138            ],
139        )

Deserialize a raw JSON of destinyUserInfo destiny membership information.

Parameters
Returns
  • aiobungie.crates.user.DestinyMembership: A Destiny 2 membership.
def deserialize_destiny_memberships( self, data: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
141    def deserialize_destiny_memberships(
142        self, data: typedefs.JSONArray
143    ) -> collections.Sequence[user.DestinyMembership]:
144        return [self.deserialize_destiny_membership(membership) for membership in data]

Deserialize a raw JSON payload/array of destinyUserInfo.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.DestinyMembership]: A sequence of Destiny 2 memberships.
def deserialize_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.User:
146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
147        primary_membership_id: typing.Optional[int] = None
148        if raw_primary_id := data.get("primaryMembershipId"):
149            primary_membership_id = int(raw_primary_id)
150
151        return user.User(
152            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
153            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
154            primary_membership_id=primary_membership_id,
155        )

Deserialize a raw JSON results of fetched user memberships and Bungie.net user its their id.

Parameters
Returns
def deserialize_searched_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.SearchableDestinyUser:
157    def deserialize_searched_user(
158        self, payload: typedefs.JSONObject
159    ) -> user.SearchableDestinyUser:
160        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
161        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
162            raw_name
163        ):
164            name = raw_name
165
166        code: typing.Optional[int] = None
167        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
168            code = int(raw_code)
169
170        bungie_id: typing.Optional[int] = None
171        if raw_bungie_id := payload.get("bungieNetMembershipId"):
172            bungie_id = int(raw_bungie_id)
173
174        return user.SearchableDestinyUser(
175            name=name,
176            code=code,
177            bungie_id=bungie_id,
178            memberships=self.deserialize_destiny_memberships(
179                payload["destinyMemberships"]
180            ),
181        )

Deserialize the results of user search details.

Parameters
Returns
def deserialize_user_credentials( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
183    def deserialize_user_credentials(
184        self, payload: typedefs.JSONArray
185    ) -> collections.Sequence[user.UserCredentials]:
186        return [
187            user.UserCredentials(
188                type=enums.CredentialType(int(creds["credentialType"])),
189                display_name=creds["credentialDisplayName"],
190                is_public=creds["isPublic"],
191                self_as_string=creds.get("credentialAsString", undefined.UNDEFINED),
192            )
193            for creds in payload
194        ]

Deserialize a JSON array of Bungie user credentials.

Parameters
Returns
def deserialize_user_themes( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
196    def deserialize_user_themes(
197        self, payload: typedefs.JSONArray
198    ) -> collections.Sequence[user.UserThemes]:
199        return [
200            user.UserThemes(
201                id=int(entry["userThemeId"]),
202                name=entry["userThemeName"]
203                if "userThemeName" in entry
204                else undefined.UNDEFINED,
205                description=entry["userThemeDescription"]
206                if "userThemeDescription" in entry
207                else undefined.UNDEFINED,
208            )
209            for entry in payload
210        ]

Deserialize a raw JSON array of Bungie user themes.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of bungie user themes.
def deserialize_clan(self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.Clan:
212    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
213        # This is kinda redundant
214        data = payload
215
216        # This is always outside the details.
217        current_user_map: typing.Optional[
218            collections.Mapping[str, clans.ClanMember]
219        ] = None
220        if raw_current_user_map := payload.get("currentUserMemberMap"):
221            current_user_map = {
222                membership_type: self.deserialize_clan_member(membership)
223                for membership_type, membership in raw_current_user_map.items()
224            }
225
226        try:
227            data = payload["detail"]
228        except KeyError:
229            pass
230
231        id = data["groupId"]
232        name = data["name"]
233        created_at = data["creationDate"]
234        member_count = data["memberCount"]
235        about = data["about"]
236        motto = data["motto"]
237        is_public = data["isPublic"]
238        banner = assets.Image(str(data["bannerPath"]))
239        avatar = assets.Image(str(data["avatarPath"]))
240        tags = data["tags"]
241        type = data["groupType"]
242
243        features = data["features"]
244        features_obj = clans.ClanFeatures(
245            max_members=features["maximumMembers"],
246            max_membership_types=features["maximumMembershipsOfGroupType"],
247            capabilities=features["capabilities"],
248            membership_types=features["membershipTypes"],
249            invite_permissions=features["invitePermissionOverride"],
250            update_banner_permissions=features["updateBannerPermissionOverride"],
251            update_culture_permissions=features["updateCulturePermissionOverride"],
252            join_level=features["joinLevel"],
253        )
254
255        information: typedefs.JSONObject = data["clanInfo"]
256        progression: collections.Mapping[int, progressions.Progression] = {
257            int(prog_hash): self.deserialize_progressions(prog)
258            for prog_hash, prog in information["d2ClanProgressions"].items()
259        }
260
261        founder: typedefs.NoneOr[clans.ClanMember] = None
262        if raw_founder := payload.get("founder"):
263            founder = self.deserialize_clan_member(raw_founder)
264
265        return clans.Clan(
266            net=self._net,
267            id=int(id),
268            name=name,
269            type=enums.GroupType(type),
270            created_at=time.clean_date(created_at),
271            member_count=member_count,
272            motto=motto,
273            about=about,
274            is_public=is_public,
275            banner=banner,
276            avatar=avatar,
277            tags=tags,
278            features=features_obj,
279            owner=founder,
280            progressions=progression,
281            call_sign=information["clanCallsign"],
282            banner_data=information["clanBannerData"],
283            chat_security=data["chatSecurity"],
284            conversation_id=int(data["conversationId"]),
285            allow_chat=data["allowChat"],
286            theme=data["theme"],
287            current_user_membership=current_user_map,
288        )

Deserialize a raw JSON payload of Bungie clan information.

Parameters
Returns
def deserialize_clan_member( self, data: dict[str, typing.Any], /) -> aiobungie.crates.clans.ClanMember:
290    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
291        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
292        return clans.ClanMember(
293            net=self._net,
294            last_seen_name=destiny_user.last_seen_name,
295            id=destiny_user.id,
296            name=destiny_user.name,
297            icon=destiny_user.icon,
298            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
299            group_id=int(data["groupId"]),
300            joined_at=time.clean_date(data["joinDate"]),
301            types=destiny_user.types,
302            is_public=destiny_user.is_public,
303            type=destiny_user.type,
304            code=destiny_user.code,
305            is_online=data["isOnline"],
306            crossave_override=destiny_user.crossave_override,
307            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
308            if "bungieNetUserInfo" in data
309            else None,
310            member_type=enums.ClanMemberType(int(data["memberType"])),
311        )

Deserialize a JSON payload of a clan member information.

Parameters
Returns
def deserialize_clan_members( self, data: dict[str, typing.Any], /) -> Iterator[aiobungie.crates.clans.ClanMember]:
313    def deserialize_clan_members(
314        self, data: typedefs.JSONObject, /
315    ) -> iterators.Iterator[clans.ClanMember]:
316        return iterators.Iterator(
317            [self.deserialize_clan_member(member) for member in data["results"]]
318        )

Deserialize a JSON payload of a clan members information.

Parameters
Returns
def deserialize_group_member( self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.GroupMember:
320    def deserialize_group_member(
321        self, payload: typedefs.JSONObject
322    ) -> clans.GroupMember:
323        member = payload["member"]
324        return clans.GroupMember(
325            net=self._net,
326            join_date=time.clean_date(member["joinDate"]),
327            group_id=int(member["groupId"]),
328            member_type=enums.ClanMemberType(member["memberType"]),
329            is_online=member["isOnline"],
330            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
331            inactive_memberships=payload.get("areAllMembershipsInactive", None),
332            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
333            group=self.deserialize_clan(payload["group"]),
334        )

Deserialize a JSON payload of group information for a member.

Parameters
Returns
def deserialize_clan_conversations( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
352    def deserialize_clan_conversations(
353        self, payload: typedefs.JSONArray
354    ) -> collections.Sequence[clans.ClanConversation]:
355        return [self._deserialize_clan_conversation(conv) for conv in payload]

Deserialize a JSON array of a clan conversations information.

Parameters
Returns
def deserialize_app_owner( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.ApplicationOwner:
357    def deserialize_app_owner(
358        self, payload: typedefs.JSONObject
359    ) -> application.ApplicationOwner:
360        return application.ApplicationOwner(
361            net=self._net,
362            name=payload.get("bungieGlobalDisplayName", undefined.UNDEFINED),
363            id=int(payload["membershipId"]),
364            type=enums.MembershipType(payload["membershipType"]),
365            icon=assets.Image(str(payload["iconPath"])),
366            is_public=payload["isPublic"],
367            code=payload.get("bungieGlobalDisplayNameCode", None),
368        )

Deserialize a JSON payload of Bungie Developer portal application owner information.

Parameters
Returns
  • aiobungie.crates.application.ApplicationOwner: An application owner.
def deserialize_app( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.Application:
370    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
371        return application.Application(
372            id=int(payload["applicationId"]),
373            name=payload["name"],
374            link=payload["link"],
375            status=payload["status"],
376            redirect_url=payload.get("redirectUrl", None),
377            created_at=time.clean_date(str(payload["creationDate"])),
378            published_at=time.clean_date(str(payload["firstPublished"])),
379            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
380            scope=payload.get("scope", undefined.UNDEFINED),
381        )

Deserialize a JSON payload of Bungie Developer portal application information.

Parameters
Returns
  • aiobungie.crates.application.Application: An application.
def deserialize_profile( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.profile.Profile:
403    def deserialize_profile(self, payload: typedefs.JSONObject, /) -> profile.Profile:
404        payload = payload["data"]
405        id = int(payload["userInfo"]["membershipId"])
406        name = payload["userInfo"]["displayName"]
407        is_public = payload["userInfo"]["isPublic"]
408        type = enums.MembershipType(payload["userInfo"]["membershipType"])
409        last_played = time.clean_date(str(payload["dateLastPlayed"]))
410        character_ids = [int(cid) for cid in payload["characterIds"]]
411        power_cap = payload["currentSeasonRewardPowerCap"]
412
413        return profile.Profile(
414            id=int(id),
415            name=name,
416            is_public=is_public,
417            type=type,
418            last_played=last_played,
419            character_ids=character_ids,
420            power_cap=power_cap,
421            net=self._net,
422        )

Deserialize a JSON payload of Bungie.net profile information.

Parameters
Returns
def deserialize_profile_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileItemImpl:
424    def deserialize_profile_item(
425        self, payload: typedefs.JSONObject
426    ) -> profile.ProfileItemImpl:
427        instance_id: typing.Optional[int] = None
428        if raw_instance_id := payload.get("itemInstanceId"):
429            instance_id = int(raw_instance_id)
430
431        version_number: typing.Optional[int] = None
432        if raw_version := payload.get("versionNumber"):
433            version_number = int(raw_version)
434
435        transfer_status = enums.TransferStatus(payload["transferStatus"])
436
437        return profile.ProfileItemImpl(
438            net=self._net,
439            hash=payload["itemHash"],
440            quantity=payload["quantity"],
441            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
442            location=enums.ItemLocation(payload["location"]),
443            bucket=payload["bucketHash"],
444            transfer_status=transfer_status,
445            lockable=payload["lockable"],
446            state=enums.ItemState(payload["state"]),
447            dismantle_permissions=payload["dismantlePermission"],
448            is_wrapper=payload["isWrapper"],
449            instance_id=instance_id,
450            version_number=version_number,
451            ornament_id=payload.get("overrideStyleItemHash"),
452        )

Deserialize a JSON payload of a singular profile component item.

Parameters
Returns
def deserialize_objectives( self, payload: dict[str, typing.Any]) -> aiobungie.crates.records.Objective:
454    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
455        return records.Objective(
456            net=self._net,
457            hash=payload["objectiveHash"],
458            visible=payload["visible"],
459            complete=payload["complete"],
460            completion_value=payload["completionValue"],
461            progress=payload.get("progress"),
462            destination_hash=payload.get("destinationHash"),
463            activity_hash=payload.get("activityHash"),
464        )

Deserialize a JSON payload of an objective found in a record profile component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
  • aiobungie.crates.records.Objective: A record objective object.
def deserialize_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, **nodes: int) -> aiobungie.crates.records.Record:
466    def deserialize_records(
467        self,
468        payload: typedefs.JSONObject,
469        scores: typing.Optional[records.RecordScores] = None,
470        **nodes: int,
471    ) -> records.Record:
472        objectives: typing.Optional[list[records.Objective]] = None
473        interval_objectives: typing.Optional[list[records.Objective]] = None
474        record_state: typedefs.IntAnd[records.RecordState]
475
476        record_state = records.RecordState(payload["state"])
477
478        if raw_objs := payload.get("objectives"):
479            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
480
481        if raw_interval_objs := payload.get("intervalObjectives"):
482            interval_objectives = [
483                self.deserialize_objectives(obj) for obj in raw_interval_objs
484            ]
485
486        return records.Record(
487            scores=scores,
488            categories_node_hash=nodes.get("categories_hash", undefined.UNDEFINED),
489            seals_node_hash=nodes.get("seals_hash", undefined.UNDEFINED),
490            state=record_state,
491            objectives=objectives,
492            interval_objectives=interval_objectives,
493            redeemed_count=payload.get("intervalsRedeemedCount", 0),
494            completion_times=payload.get("completedCount", None),
495            reward_visibility=payload.get("rewardVisibility"),
496        )

Deserialize a JSON object of a profile record component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
  • scores (typing.Optional[records.RecordScores]): The records scores object. This exists only to keep the signature of aiobungie.crates.CharacterRecord with the record object. As it will always be None in that object.
  • **nodes (int): An int kwargs use to grab the node hashes while deserializing components.
Returns
  • aiobungie.records.Record: A standard implementation of a profile record component.
def deserialize_character_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, record_hashes: Optional[list[int]] = None) -> aiobungie.crates.records.CharacterRecord:
498    def deserialize_character_records(
499        self,
500        payload: typedefs.JSONObject,
501        scores: typing.Optional[records.RecordScores] = None,
502        record_hashes: typing.Optional[list[int]] = None,
503    ) -> records.CharacterRecord:
504        record = self.deserialize_records(payload, scores)
505        return records.CharacterRecord(
506            scores=scores,
507            categories_node_hash=record.categories_node_hash,
508            seals_node_hash=record.seals_node_hash,
509            state=record.state,
510            objectives=record.objectives,
511            interval_objectives=record.interval_objectives,
512            redeemed_count=payload.get("intervalsRedeemedCount", 0),
513            completion_times=payload.get("completedCount"),
514            reward_visibility=payload.get("rewardVisibility"),
515            record_hashes=record_hashes or [],
516        )

Deserialize a JSON object of a profile character record component.

This almost does the same this as deserialize_records but has more fields which can only be found in a character record.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
Returns
  • aiobungie.records.CharacterRecord: A standard implementation of a profile character record component.
def deserialize_character_dye(self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Dye:
518    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
519        return character.Dye(
520            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
521        )

Deserialize a JSON payload of a character's dye information.

Parameters
Returns
  • aiobungie.crates.character.Dye: Information about a character dye object.
def deserialize_character_customization( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CustomizationOptions:
523    def deserialize_character_customization(
524        self, payload: typedefs.JSONObject
525    ) -> character.CustomizationOptions:
526        return character.CustomizationOptions(
527            personality=payload["personality"],
528            face=payload["face"],
529            skin_color=payload["skinColor"],
530            lip_color=payload["lipColor"],
531            eye_color=payload["eyeColor"],
532            hair_colors=payload.get("hairColors", []),
533            feature_colors=payload.get("featureColors", []),
534            decal_color=payload["decalColor"],
535            wear_helmet=payload["wearHelmet"],
536            hair_index=payload["hairIndex"],
537            feature_index=payload["featureIndex"],
538            decal_index=payload["decalIndex"],
539        )

Deserialize a JSON payload of a character customization information found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.CustomizationOptions: Information about a character customs object.
def deserialize_character_minimal_equipments( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.MinimalEquipments:
541    def deserialize_character_minimal_equipments(
542        self, payload: typedefs.JSONObject
543    ) -> character.MinimalEquipments:
544        dyes = None
545        if raw_dyes := payload.get("dyes"):
546            if raw_dyes:
547                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
548        return character.MinimalEquipments(
549            net=self._net, item_hash=payload["itemHash"], dyes=dyes
550        )

Deserialize a singular JSON peer view of equipment found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.MinimalEquipments: A minimal equipment object.
def deserialize_character_render_data( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.character.RenderedData:
552    def deserialize_character_render_data(
553        self, payload: typedefs.JSONObject, /
554    ) -> character.RenderedData:
555        return character.RenderedData(
556            net=self._net,
557            customization=self.deserialize_character_customization(
558                payload["customization"]
559            ),
560            custom_dyes=[
561                self.deserialize_character_dye(dye)
562                for dye in payload["customDyes"]
563                if dye
564            ],
565            equipment=[
566                self.deserialize_character_minimal_equipments(equipment)
567                for equipment in payload["peerView"]["equipment"]
568            ],
569        )

Deserialize a JSON payload of a profile character render data component.

Parameters
Returns
def deserialize_available_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AvailableActivity:
571    def deserialize_available_activity(
572        self, payload: typedefs.JSONObject
573    ) -> activity.AvailableActivity:
574        return activity.AvailableActivity(
575            hash=payload["activityHash"],
576            is_new=payload["isNew"],
577            is_completed=payload["isCompleted"],
578            is_visible=payload["isVisible"],
579            display_level=payload.get("displayLevel"),
580            recommended_light=payload.get("recommendedLight"),
581            difficulty=activity.Difficulty(payload["difficultyTier"]),
582            can_join=payload["canJoin"],
583            can_lead=payload["canLead"],
584        )

Deserialize a JSON payload of an available activities.

This method is used to deserialize an array of aiobungie.crates.CharacterActivity.available_activities.

Parameters
Returns
def deserialize_character_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.CharacterActivity:
586    def deserialize_character_activity(
587        self, payload: typedefs.JSONObject
588    ) -> activity.CharacterActivity:
589        current_mode: typing.Optional[enums.GameMode] = None
590        if raw_current_mode := payload.get("currentActivityModeType"):
591            current_mode = enums.GameMode(raw_current_mode)
592
593        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
594        if raw_current_modes := payload.get("currentActivityModeTypes"):
595            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
596
597        return activity.CharacterActivity(
598            date_started=time.clean_date(payload["dateActivityStarted"]),
599            current_hash=payload["currentActivityHash"],
600            current_mode_hash=payload["currentActivityModeHash"],
601            current_mode=current_mode,
602            current_mode_hashes=payload.get("currentActivityModeHashes"),
603            current_mode_types=current_mode_types,
604            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
605            last_story_hash=payload["lastCompletedStoryHash"],
606            available_activities=[
607                self.deserialize_available_activity(activity_)
608                for activity_ in payload["availableActivities"]
609            ],
610        )

Deserialize a JSON payload of character activity profile component.

Parameters
Returns
def deserialize_profile_items( self, payload: dict[str, typing.Any], /) -> list[aiobungie.crates.profile.ProfileItemImpl]:
612    def deserialize_profile_items(
613        self, payload: typedefs.JSONObject, /
614    ) -> list[profile.ProfileItemImpl]:
615        return [self.deserialize_profile_item(item) for item in payload["items"]]

Deserialize a JSON payload of profile items component information.

This may deserialize profileInventories or profileCurrencies or any other alternatives.

Parameters
Returns
def deserialize_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.progressions.Progression:
658    def deserialize_progressions(
659        self, payload: typedefs.JSONObject
660    ) -> progressions.Progression:
661        return progressions.Progression(
662            hash=int(payload["progressionHash"]),
663            level=int(payload["level"]),
664            cap=int(payload["levelCap"]),
665            daily_limit=int(payload["dailyLimit"]),
666            weekly_limit=int(payload["weeklyLimit"]),
667            current_progress=int(payload["currentProgress"]),
668            daily_progress=int(payload["dailyProgress"]),
669            needed=int(payload["progressToNextLevel"]),
670            next_level=int(payload["nextLevelAt"]),
671        )
def deserialize_milestone( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.Milestone:
758    def deserialize_milestone(
759        self, payload: typedefs.JSONObject
760    ) -> milestones.Milestone:
761        start_date: typing.Optional[datetime.datetime] = None
762        if raw_start_date := payload.get("startDate"):
763            start_date = time.clean_date(raw_start_date)
764
765        end_date: typing.Optional[datetime.datetime] = None
766        if raw_end_date := payload.get("endDate"):
767            end_date = time.clean_date(raw_end_date)
768
769        rewards: typing.Optional[
770            collections.Collection[milestones.MilestoneReward]
771        ] = None
772        if raw_rewards := payload.get("rewards"):
773            rewards = [
774                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
775            ]
776
777        activities: typing.Optional[
778            collections.Sequence[milestones.MilestoneActivity]
779        ] = None
780        if raw_activities := payload.get("activities"):
781            activities = [
782                self._deserialize_milestone_activity(active)
783                for active in raw_activities
784            ]
785
786        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
787        if raw_quests := payload.get("availableQuests"):
788            quests = [
789                self._deserialize_milestone_available_quest(quest)
790                for quest in raw_quests
791            ]
792
793        vendors: typing.Optional[
794            collections.Sequence[milestones.MilestoneVendor]
795        ] = None
796        if raw_vendors := payload.get("vendors"):
797            vendors = [
798                milestones.MilestoneVendor(
799                    vendor_hash=vendor["vendorHash"],
800                    preview_itemhash=vendor.get("previewItemHash"),
801                )
802                for vendor in raw_vendors
803            ]
804
805        return milestones.Milestone(
806            hash=payload["milestoneHash"],
807            start_date=start_date,
808            end_date=end_date,
809            order=payload["order"],
810            rewards=rewards,
811            available_quests=quests,
812            activities=activities,
813            vendors=vendors,
814        )
def deserialize_characters( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.Character]:
831    def deserialize_characters(
832        self, payload: typedefs.JSONObject
833    ) -> collections.Mapping[int, character.Character]:
834        return {
835            int(char_id): self._set_character_attrs(char)
836            for char_id, char in payload["data"].items()
837        }
def deserialize_character( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Character:
839    def deserialize_character(
840        self, payload: typedefs.JSONObject
841    ) -> character.Character:
842        return self._set_character_attrs(payload)
def deserialize_character_equipments( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, collections.abc.Sequence[aiobungie.crates.profile.ProfileItemImpl]]:
844    def deserialize_character_equipments(
845        self, payload: typedefs.JSONObject
846    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
847        return {
848            int(char_id): self.deserialize_profile_items(item)
849            for char_id, item in payload["data"].items()
850        }
def deserialize_character_activities( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.activity.CharacterActivity]:
852    def deserialize_character_activities(
853        self, payload: typedefs.JSONObject
854    ) -> collections.Mapping[int, activity.CharacterActivity]:
855        return {
856            int(char_id): self.deserialize_character_activity(data)
857            for char_id, data in payload["data"].items()
858        }
def deserialize_characters_render_data( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.RenderedData]:
860    def deserialize_characters_render_data(
861        self, payload: typedefs.JSONObject
862    ) -> collections.Mapping[int, character.RenderedData]:
863        return {
864            int(char_id): self.deserialize_character_render_data(data)
865            for char_id, data in payload["data"].items()
866        }
def deserialize_character_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CharacterProgression:
868    def deserialize_character_progressions(
869        self, payload: typedefs.JSONObject
870    ) -> character.CharacterProgression:
871        progressions_ = {
872            int(prog_id): self.deserialize_progressions(prog)
873            for prog_id, prog in payload["progressions"].items()
874        }
875
876        factions = {
877            int(faction_id): self._deserialize_factions(faction)
878            for faction_id, faction in payload["factions"].items()
879        }
880
881        milestones_ = {
882            int(milestone_hash): self.deserialize_milestone(milestone)
883            for milestone_hash, milestone in payload["milestones"].items()
884        }
885
886        uninstanced_item_objectives = {
887            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
888            for item_hash, obj in payload["uninstancedItemObjectives"].items()
889        }
890
891        artifact = payload["seasonalArtifact"]
892        seasonal_artifact = season.CharacterScopedArtifact(
893            hash=artifact["artifactHash"],
894            points_used=artifact["pointsUsed"],
895            reset_count=artifact["resetCount"],
896            tiers=[
897                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
898            ],
899        )
900        checklists = payload["checklists"]
901
902        return character.CharacterProgression(
903            progressions=progressions_,
904            factions=factions,
905            checklists=checklists,
906            milestones=milestones_,
907            seasonal_artifact=seasonal_artifact,
908            uninstanced_item_objectives=uninstanced_item_objectives,
909        )
def deserialize_character_progressions_mapping( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.CharacterProgression]:
911    def deserialize_character_progressions_mapping(
912        self, payload: typedefs.JSONObject
913    ) -> collections.Mapping[int, character.CharacterProgression]:
914        character_progressions: collections.Mapping[
915            int, character.CharacterProgression
916        ] = {}
917        for char_id, data in payload["data"].items():
918            # A little hack to stop mypy complaining about Mapping <-> dict
919            character_progressions[
920                int(char_id)
921            ] = self.deserialize_character_progressions(data)  # type: ignore[index]
922        return character_progressions
def deserialize_characters_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.CharacterRecord]:
924    def deserialize_characters_records(
925        self,
926        payload: typedefs.JSONObject,
927    ) -> collections.Mapping[int, records.CharacterRecord]:
928        return {
929            int(rec_id): self.deserialize_character_records(
930                rec, record_hashes=payload.get("featuredRecordHashes")
931            )
932            for rec_id, rec in payload["records"].items()
933        }
def deserialize_profile_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.Record]:
935    def deserialize_profile_records(
936        self, payload: typedefs.JSONObject
937    ) -> collections.Mapping[int, records.Record]:
938        raw_profile_records = payload["data"]
939        scores = records.RecordScores(
940            current_score=raw_profile_records["score"],
941            legacy_score=raw_profile_records["legacyScore"],
942            lifetime_score=raw_profile_records["lifetimeScore"],
943        )
944        return {
945            int(record_id): self.deserialize_records(
946                record,
947                scores,
948                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
949                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
950            )
951            for record_id, record in raw_profile_records["records"].items()
952        }
def deserialize_craftables_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CraftablesComponent:
987    def deserialize_craftables_component(
988        self, payload: typedefs.JSONObject
989    ) -> components.CraftablesComponent:
990        return components.CraftablesComponent(
991            net=self._net,
992            craftables={
993                int(item_id): self._deserialize_craftable_item(item)
994                for item_id, item in payload["craftables"].items()
995                if item is not None
996            },
997            crafting_root_node_hash=payload["craftingRootNodeHash"],
998        )
def deserialize_components( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.Component:
1000    def deserialize_components(  # noqa: C901 Too complex.
1001        self, payload: typedefs.JSONObject
1002    ) -> components.Component:
1003        profile_: typing.Optional[profile.Profile] = None
1004        if raw_profile := payload.get("profile"):
1005            profile_ = self.deserialize_profile(raw_profile)
1006
1007        profile_progression: typing.Optional[profile.ProfileProgression] = None
1008        if raw_profile_progression := payload.get("profileProgression"):
1009            profile_progression = self.deserialize_profile_progression(
1010                raw_profile_progression
1011            )
1012
1013        profile_currencies: typing.Optional[
1014            collections.Sequence[profile.ProfileItemImpl]
1015        ] = None
1016        if raw_profile_currencies := payload.get("profileCurrencies"):
1017            if "data" in raw_profile_currencies:
1018                profile_currencies = self.deserialize_profile_items(
1019                    raw_profile_currencies["data"]
1020                )
1021
1022        profile_inventories: typing.Optional[
1023            collections.Sequence[profile.ProfileItemImpl]
1024        ] = None
1025        if raw_profile_inventories := payload.get("profileInventory"):
1026            if "data" in raw_profile_inventories:
1027                profile_inventories = self.deserialize_profile_items(
1028                    raw_profile_inventories["data"]
1029                )
1030
1031        profile_records: typing.Optional[
1032            collections.Mapping[int, records.Record]
1033        ] = None
1034
1035        if raw_profile_records_ := payload.get("profileRecords"):
1036            profile_records = self.deserialize_profile_records(raw_profile_records_)
1037
1038        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1039        if raw_characters := payload.get("characters"):
1040            characters = self.deserialize_characters(raw_characters)
1041
1042        character_records: typing.Optional[
1043            collections.Mapping[int, records.CharacterRecord]
1044        ] = None
1045
1046        if raw_character_records := payload.get("characterRecords"):
1047            # Had to do it in two steps..
1048            to_update: typedefs.JSONObject = {}
1049            for _, data in raw_character_records["data"].items():
1050                for record_id, record in data.items():
1051                    to_update[record_id] = record
1052
1053            character_records = {
1054                int(rec_id): self.deserialize_character_records(
1055                    rec, record_hashes=to_update.get("featuredRecordHashes")
1056                )
1057                for rec_id, rec in to_update["records"].items()
1058            }
1059
1060        character_equipments: typing.Optional[
1061            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1062        ] = None
1063        if raw_character_equips := payload.get("characterEquipment"):
1064            character_equipments = self.deserialize_character_equipments(
1065                raw_character_equips
1066            )
1067
1068        character_inventories: typing.Optional[
1069            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1070        ] = None
1071        if raw_character_inventories := payload.get("characterInventories"):
1072            if "data" in raw_character_inventories:
1073                character_inventories = self.deserialize_character_equipments(
1074                    raw_character_inventories
1075                )
1076
1077        character_activities: typing.Optional[
1078            collections.Mapping[int, activity.CharacterActivity]
1079        ] = None
1080        if raw_char_acts := payload.get("characterActivities"):
1081            character_activities = self.deserialize_character_activities(raw_char_acts)
1082
1083        character_render_data: typing.Optional[
1084            collections.Mapping[int, character.RenderedData]
1085        ] = None
1086        if raw_character_render_data := payload.get("characterRenderData"):
1087            character_render_data = self.deserialize_characters_render_data(
1088                raw_character_render_data
1089            )
1090
1091        character_progressions: typing.Optional[
1092            collections.Mapping[int, character.CharacterProgression]
1093        ] = None
1094
1095        if raw_character_progressions := payload.get("characterProgressions"):
1096            character_progressions = self.deserialize_character_progressions_mapping(
1097                raw_character_progressions
1098            )
1099
1100        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1101        if raw_profile_string_vars := payload.get("profileStringVariables"):
1102            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1103
1104        character_string_vars: typing.Optional[
1105            collections.Mapping[int, collections.Mapping[int, int]]
1106        ] = None
1107        if raw_character_string_vars := payload.get("characterStringVariables"):
1108            character_string_vars = {
1109                int(char_id): data["integerValuesByHash"]
1110                for char_id, data in raw_character_string_vars["data"].items()
1111            }
1112
1113        metrics: typing.Optional[
1114            collections.Sequence[
1115                collections.Mapping[
1116                    int, tuple[bool, typing.Optional[records.Objective]]
1117                ]
1118            ]
1119        ] = None
1120        root_node_hash: typing.Optional[int] = None
1121
1122        if raw_metrics := payload.get("metrics"):
1123            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1124            metrics = [
1125                {
1126                    int(metrics_hash): (
1127                        data["invisible"],
1128                        self.deserialize_objectives(data["objectiveProgress"])
1129                        if "objectiveProgress" in data
1130                        else None,
1131                    )
1132                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1133                }
1134            ]
1135        transitory: typing.Optional[fireteams.FireteamParty] = None
1136        if raw_transitory := payload.get("profileTransitoryData"):
1137            if "data" in raw_transitory:
1138                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1139
1140        item_components: typing.Optional[components.ItemsComponent] = None
1141        if raw_item_components := payload.get("itemComponents"):
1142            item_components = self.deserialize_items_component(raw_item_components)
1143
1144        profile_plugsets: typing.Optional[
1145            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1146        ] = None
1147
1148        if raw_profile_plugs := payload.get("profilePlugSets"):
1149            profile_plugsets = {
1150                int(index): [self.deserialize_plug_item_state(state) for state in data]
1151                for index, data in raw_profile_plugs["data"]["plugs"].items()
1152            }
1153
1154        character_plugsets: typing.Optional[
1155            collections.Mapping[
1156                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1157            ]
1158        ] = None
1159        if raw_char_plugsets := payload.get("characterPlugSets"):
1160            character_plugsets = {
1161                int(char_id): {
1162                    int(index): [
1163                        self.deserialize_plug_item_state(state) for state in data
1164                    ]
1165                    for index, data in inner["plugs"].items()
1166                }
1167                for char_id, inner in raw_char_plugsets["data"].items()
1168            }
1169
1170        character_collectibles: typing.Optional[
1171            collections.Mapping[int, items.Collectible]
1172        ] = None
1173        if raw_character_collectibles := payload.get("characterCollectibles"):
1174            character_collectibles = {
1175                int(char_id): self._deserialize_collectible(data)
1176                for char_id, data in raw_character_collectibles["data"].items()
1177            }
1178
1179        profile_collectibles: typing.Optional[items.Collectible] = None
1180        if raw_profile_collectibles := payload.get("profileCollectibles"):
1181            profile_collectibles = self._deserialize_collectible(
1182                raw_profile_collectibles["data"]
1183            )
1184
1185        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1186        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1187            profile_nodes = {
1188                int(node_hash): self._deserialize_node(node)
1189                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1190            }
1191
1192        character_nodes: typing.Optional[
1193            collections.Mapping[int, collections.Mapping[int, records.Node]]
1194        ] = None
1195        if raw_character_nodes := payload.get("characterPresentationNodes"):
1196            character_nodes = {
1197                int(char_id): {
1198                    int(node_hash): self._deserialize_node(node)
1199                    for node_hash, node in each_character["nodes"].items()
1200                }
1201                for char_id, each_character in raw_character_nodes["data"].items()
1202            }
1203
1204        platform_silver: typing.Optional[
1205            collections.Mapping[str, profile.ProfileItemImpl]
1206        ] = None
1207        if raw_platform_silver := payload.get("platformSilver"):
1208            if "data" in raw_platform_silver:
1209                platform_silver = {
1210                    platform_name: self.deserialize_profile_item(item)
1211                    for platform_name, item in raw_platform_silver["data"][
1212                        "platformSilver"
1213                    ].items()
1214                }
1215
1216        character_currency_lookups: typing.Optional[
1217            collections.Mapping[int, collections.Sequence[items.Currency]]
1218        ] = None
1219        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1220            if "data" in raw_char_lookups:
1221                character_currency_lookups = {
1222                    int(char_id): self._deserialize_currencies(currency)
1223                    for char_id, currency in raw_char_lookups["data"].items()
1224                }
1225
1226        character_craftables: typing.Optional[
1227            collections.Mapping[int, components.CraftablesComponent]
1228        ] = None
1229        if raw_character_craftables := payload.get("characterCraftables"):
1230            if "data" in raw_character_craftables:
1231                character_craftables = {
1232                    int(char_id): self.deserialize_craftables_component(craftable)
1233                    for char_id, craftable in raw_character_craftables["data"].items()
1234                }
1235
1236        return components.Component(
1237            profiles=profile_,
1238            profile_progression=profile_progression,
1239            profile_currencies=profile_currencies,
1240            profile_inventories=profile_inventories,
1241            profile_records=profile_records,
1242            characters=characters,
1243            character_records=character_records,
1244            character_equipments=character_equipments,
1245            character_inventories=character_inventories,
1246            character_activities=character_activities,
1247            character_render_data=character_render_data,
1248            character_progressions=character_progressions,
1249            profile_string_variables=profile_string_vars,
1250            character_string_variables=character_string_vars,
1251            metrics=metrics,
1252            root_node_hash=root_node_hash,
1253            transitory=transitory,
1254            item_components=item_components,
1255            profile_plugsets=profile_plugsets,
1256            character_plugsets=character_plugsets,
1257            character_collectibles=character_collectibles,
1258            profile_collectibles=profile_collectibles,
1259            profile_nodes=profile_nodes,
1260            character_nodes=character_nodes,
1261            platform_silver=platform_silver,
1262            character_currency_lookups=character_currency_lookups,
1263            character_craftables=character_craftables,
1264        )

Deserialize a JSON payload of Bungie.net profile components information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_items_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.ItemsComponent:
1266    def deserialize_items_component(
1267        self, payload: typedefs.JSONObject
1268    ) -> components.ItemsComponent:
1269        instances: typing.Optional[
1270            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1271        ] = None
1272        if raw_instances := payload.get("instances"):
1273            instances = [
1274                {
1275                    int(ins_id): self.deserialize_instanced_item(item)
1276                    for ins_id, item in raw_instances["data"].items()
1277                }
1278            ]
1279
1280        render_data: typing.Optional[
1281            collections.Mapping[int, tuple[bool, dict[int, int]]]
1282        ] = None
1283        if raw_render_data := payload.get("renderData"):
1284            render_data = {
1285                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1286                for ins_id, data in raw_render_data["data"].items()
1287            }
1288
1289        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1290        if raw_stats := payload.get("stats"):
1291            builder: collections.Mapping[int, items.ItemStatsView] = {}
1292            for ins_id, stat in raw_stats["data"].items():
1293                for _, items_ in stat.items():
1294                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1295            stats = builder
1296
1297        sockets: typing.Optional[
1298            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1299        ] = None
1300        if raw_sockets := payload.get("sockets"):
1301            sockets = {
1302                int(ins_id): [
1303                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1304                ]
1305                for ins_id, item in raw_sockets["data"].items()
1306            }
1307
1308        objectives: typing.Optional[
1309            collections.Mapping[int, collections.Sequence[records.Objective]]
1310        ] = None
1311        if raw_objectives := payload.get("objectives"):
1312            objectives = {
1313                int(ins_id): [self.deserialize_objectives(objective)]
1314                for ins_id, data in raw_objectives["data"].items()
1315                for objective in data["objectives"]
1316            }
1317
1318        perks: typing.Optional[
1319            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1320        ] = None
1321        if raw_perks := payload.get("perks"):
1322            perks = {
1323                int(ins_id): [
1324                    self.deserialize_item_perk(perk) for perk in item["perks"]
1325                ]
1326                for ins_id, item in raw_perks["data"].items()
1327            }
1328
1329        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1330        if raw_plug_states := payload.get("plugStates"):
1331            pending_states: list[items.PlugItemState] = []
1332            for _, plug in raw_plug_states["data"].items():
1333                pending_states.append(self.deserialize_plug_item_state(plug))
1334            plug_states = pending_states
1335
1336        reusable_plugs: typing.Optional[
1337            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1338        ] = None
1339        if raw_re_plugs := payload.get("reusablePlugs"):
1340            reusable_plugs = {
1341                int(ins_id): [
1342                    self.deserialize_plug_item_state(state) for state in inner
1343                ]
1344                for ins_id, plug in raw_re_plugs["data"].items()
1345                for inner in list(plug["plugs"].values())
1346            }
1347
1348        plug_objectives: typing.Optional[
1349            collections.Mapping[
1350                int, collections.Mapping[int, collections.Collection[records.Objective]]
1351            ]
1352        ] = None
1353        if raw_plug_objectives := payload.get("plugObjectives"):
1354            plug_objectives = {
1355                int(ins_id): {
1356                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1357                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1358                }
1359                for ins_id, inner in raw_plug_objectives["data"].items()
1360            }
1361
1362        return components.ItemsComponent(
1363            sockets=sockets,
1364            stats=stats,
1365            render_data=render_data,
1366            instances=instances,
1367            objectives=objectives,
1368            perks=perks,
1369            plug_states=plug_states,
1370            reusable_plugs=reusable_plugs,
1371            plug_objectives=plug_objectives,
1372        )

Deserialize a JSON objects within the itemComponents key.`

def deserialize_character_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CharacterComponent:
1374    def deserialize_character_component(  # type: ignore[call-arg]
1375        self, payload: typedefs.JSONObject
1376    ) -> components.CharacterComponent:
1377        character_: typing.Optional[character.Character] = None
1378        if raw_singular_character := payload.get("character"):
1379            character_ = self.deserialize_character(raw_singular_character["data"])
1380
1381        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1382        if raw_inventory := payload.get("inventory"):
1383            if "data" in raw_inventory:
1384                inventory = self.deserialize_profile_items(raw_inventory["data"])
1385
1386        activities: typing.Optional[activity.CharacterActivity] = None
1387        if raw_activities := payload.get("activities"):
1388            activities = self.deserialize_character_activity(raw_activities["data"])
1389
1390        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1391        if raw_equipments := payload.get("equipment"):
1392            equipment = self.deserialize_profile_items(raw_equipments["data"])
1393
1394        progressions_: typing.Optional[character.CharacterProgression] = None
1395        if raw_progressions := payload.get("progressions"):
1396            progressions_ = self.deserialize_character_progressions(
1397                raw_progressions["data"]
1398            )
1399
1400        render_data: typing.Optional[character.RenderedData] = None
1401        if raw_render_data := payload.get("renderData"):
1402            render_data = self.deserialize_character_render_data(
1403                raw_render_data["data"]
1404            )
1405
1406        character_records: typing.Optional[
1407            collections.Mapping[int, records.CharacterRecord]
1408        ] = None
1409        if raw_char_records := payload.get("records"):
1410            character_records = self.deserialize_characters_records(
1411                raw_char_records["data"]
1412            )
1413
1414        item_components: typing.Optional[components.ItemsComponent] = None
1415        if raw_item_components := payload.get("itemComponents"):
1416            item_components = self.deserialize_items_component(raw_item_components)
1417
1418        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1419        if raw_nodes := payload.get("presentationNodes"):
1420            nodes = {
1421                int(node_hash): self._deserialize_node(node)
1422                for node_hash, node in raw_nodes["data"]["nodes"].items()
1423            }
1424
1425        collectibles: typing.Optional[items.Collectible] = None
1426        if raw_collectibles := payload.get("collectibles"):
1427            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1428
1429        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1430        if raw_currencies := payload.get("currencyLookups"):
1431            if "data" in raw_currencies:
1432                currency_lookups = self._deserialize_currencies(raw_currencies)
1433
1434        return components.CharacterComponent(
1435            activities=activities,
1436            equipment=equipment,
1437            inventory=inventory,
1438            progressions=progressions_,
1439            render_data=render_data,
1440            character=character_,
1441            character_records=character_records,
1442            profile_records=None,
1443            item_components=item_components,
1444            currency_lookups=currency_lookups,
1445            collectibles=collectibles,
1446            nodes=nodes,
1447        )

Deserialize a JSON payload of Destiny 2 character component.

Parameters
Returns
def deserialize_inventory_results( self, payload: dict[str, typing.Any]) -> Iterator[aiobungie.crates.entity.SearchableEntity]:
1474    def deserialize_inventory_results(
1475        self, payload: typedefs.JSONObject
1476    ) -> iterators.Iterator[entity.SearchableEntity]:
1477        suggested_words: list[str] = payload["suggestedWords"]
1478
1479        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1480            return s if not typedefs.is_unknown(s) else undefined.UNDEFINED
1481
1482        return iterators.Iterator(
1483            [
1484                entity.SearchableEntity(
1485                    net=self._net,
1486                    hash=data["hash"],
1487                    entity_type=data["entityType"],
1488                    weight=data["weight"],
1489                    suggested_words=suggested_words,
1490                    name=data["displayProperties"]["name"],
1491                    has_icon=data["displayProperties"]["hasIcon"],
1492                    description=_check_unknown(
1493                        data["displayProperties"]["description"]
1494                    ),
1495                    icon=assets.Image(data["displayProperties"]["icon"]),
1496                )
1497                for data in payload["results"]["results"]
1498            ]
1499        )

Deserialize results of searched Destiny2 entities.

Parameters
Returns
def deserialize_inventory_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.InventoryEntity:
1528    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1529        self, payload: typedefs.JSONObject, /
1530    ) -> entity.InventoryEntity:
1531        props = self._set_entity_attrs(payload)
1532        objects = self._deserialize_inventory_item_objects(payload)
1533
1534        collectible_hash: typing.Optional[int] = None
1535        if raw_collectible_hash := payload.get("collectibleHash"):
1536            collectible_hash = int(raw_collectible_hash)
1537
1538        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1539        if raw_second_icon := payload.get("secondaryIcon"):
1540            secondary_icon = assets.Image(raw_second_icon)
1541
1542        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1543        if raw_second_overlay := payload.get("secondaryOverlay"):
1544            secondary_overlay = assets.Image(raw_second_overlay)
1545
1546        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1547        if raw_second_special := payload.get("secondarySpecial"):
1548            secondary_special = assets.Image(raw_second_special)
1549
1550        screenshot: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1551        if raw_screenshot := payload.get("screenshot"):
1552            screenshot = assets.Image(raw_screenshot)
1553
1554        watermark_icon: typing.Optional[assets.Image] = None
1555        if raw_watermark_icon := payload.get("iconWatermark"):
1556            watermark_icon = assets.Image(raw_watermark_icon)
1557
1558        watermark_shelved: typing.Optional[assets.Image] = None
1559        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1560            watermark_shelved = assets.Image(raw_watermark_shelved)
1561
1562        about: undefined.UndefinedOr[str] = undefined.UNDEFINED
1563        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1564            raw_about
1565        ):
1566            about = raw_about
1567
1568        ui_item_style: undefined.UndefinedOr[str] = undefined.UNDEFINED
1569        if (
1570            raw_ui_style := payload.get("uiItemDisplayStyle")
1571        ) and not typedefs.is_unknown(raw_ui_style):
1572            ui_item_style = raw_ui_style
1573
1574        tier_and_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1575        if (
1576            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1577        ) and not typedefs.is_unknown(raw_tier_and_name):
1578            tier_and_name = raw_tier_and_name
1579
1580        type_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1581        if (
1582            raw_type_name := payload.get("itemTypeDisplayName")
1583        ) and not typedefs.is_unknown(raw_type_name):
1584            type_name = raw_type_name
1585
1586        display_source: undefined.UndefinedOr[str] = undefined.UNDEFINED
1587        if (
1588            raw_display_source := payload.get("displaySource")
1589        ) and not typedefs.is_unknown(raw_display_source):
1590            display_source = raw_display_source
1591
1592        lorehash: typing.Optional[int] = None
1593        if raw_lore_hash := payload.get("loreHash"):
1594            lorehash = int(raw_lore_hash)
1595
1596        summary_hash: typing.Optional[int] = None
1597        if raw_summary_hash := payload.get("summaryItemHash"):
1598            summary_hash = raw_summary_hash
1599
1600        breaker_type_hash: typing.Optional[int] = None
1601        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1602            breaker_type_hash = int(raw_breaker_type_hash)
1603
1604        damage_types: typing.Optional[collections.Sequence[int]] = None
1605        if raw_damage_types := payload.get("damageTypes"):
1606            damage_types = [int(type_) for type_ in raw_damage_types]
1607
1608        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1609        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1610            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1611
1612        default_damagetype_hash: typing.Optional[int] = None
1613        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1614            default_damagetype_hash = int(raw_defaultdmg_hash)
1615
1616        emblem_objective_hash: typing.Optional[int] = None
1617        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1618            emblem_objective_hash = int(raw_emblem_obj_hash)
1619
1620        tier_type: typing.Optional[enums.TierType] = None
1621        tier: typing.Optional[enums.ItemTier] = None
1622        bucket_hash: typing.Optional[int] = None
1623        recovery_hash: typing.Optional[int] = None
1624        tier_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1625        isinstance_item: bool = False
1626        expire_tool_tip: undefined.UndefinedOr[str] = undefined.UNDEFINED
1627        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.UNDEFINED
1628        suppress_expiration: bool = False
1629        max_stack_size: typing.Optional[int] = None
1630        stack_label: undefined.UndefinedOr[str] = undefined.UNDEFINED
1631
1632        if inventory := payload.get("inventory"):
1633            tier_type = enums.TierType(int(inventory["tierType"]))
1634            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1635            bucket_hash = int(inventory["bucketTypeHash"])
1636            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1637            tier_name = inventory["tierTypeName"]
1638            isinstance_item = inventory["isInstanceItem"]
1639            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1640            max_stack_size = int(inventory["maxStackSize"])
1641
1642            try:
1643                stack_label = inventory["stackUniqueLabel"]
1644            except KeyError:
1645                pass
1646
1647        return entity.InventoryEntity(
1648            net=self._net,
1649            collectible_hash=collectible_hash,
1650            name=props.name,
1651            about=about,
1652            emblem_objective_hash=emblem_objective_hash,
1653            suppress_expiration=suppress_expiration,
1654            max_stack_size=max_stack_size,
1655            stack_label=stack_label,
1656            tier=tier,
1657            tier_type=tier_type,
1658            tier_name=tier_name,
1659            bucket_hash=bucket_hash,
1660            recovery_bucket_hash=recovery_hash,
1661            isinstance_item=isinstance_item,
1662            expire_in_orbit_message=expire_in_orbit_message,
1663            expiration_tooltip=expire_tool_tip,
1664            lore_hash=lorehash,
1665            type_and_tier_name=tier_and_name,
1666            summary_hash=summary_hash,
1667            ui_display_style=ui_item_style,
1668            type_name=type_name,
1669            breaker_type_hash=breaker_type_hash,
1670            description=props.description,
1671            display_source=display_source,
1672            hash=props.hash,
1673            damage_types=damage_types,
1674            index=props.index,
1675            icon=props.icon,
1676            has_icon=props.has_icon,
1677            screenshot=screenshot,
1678            watermark_icon=watermark_icon,
1679            watermark_shelved=watermark_shelved,
1680            secondary_icon=secondary_icon,
1681            secondary_overlay=secondary_overlay,
1682            secondary_special=secondary_special,
1683            type=enums.ItemType(int(payload["itemType"])),
1684            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1685            trait_ids=[trait for trait in payload.get("traitIds", [])],
1686            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1687            item_class=enums.Class(int(payload["classType"])),
1688            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1689            breaker_type=int(payload["breakerType"]),
1690            default_damagetype=int(payload["defaultDamageType"]),
1691            default_damagetype_hash=default_damagetype_hash,
1692            damagetype_hashes=damagetype_hashes,
1693            tooltip_notifications=payload["tooltipNotifications"],
1694            not_transferable=payload["nonTransferrable"],
1695            allow_actions=payload["allowActions"],
1696            is_equippable=payload["equippable"],
1697            objects=objects,
1698            background_colors=payload.get("backgroundColor", {}),
1699            season_hash=payload.get("seasonHash"),
1700            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1701        )

Deserialize a JSON payload of an inventory entity item information.

This can be any item from DestinyInventoryItemDefinition definition.

Parameters
Returns
def deserialize_objective_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.ObjectiveEntity:
1703    def deserialize_objective_entity(
1704        self, payload: typedefs.JSONObject, /
1705    ) -> entity.ObjectiveEntity:
1706        props = self._set_entity_attrs(payload)
1707        return entity.ObjectiveEntity(
1708            net=self._net,
1709            hash=props.hash,
1710            index=props.index,
1711            description=props.description,
1712            name=props.name,
1713            has_icon=props.has_icon,
1714            icon=props.icon,
1715            unlock_value_hash=payload["unlockValueHash"],
1716            completion_value=payload["completionValue"],
1717            scope=entity.GatingScope(int(payload["scope"])),
1718            location_hash=payload["locationHash"],
1719            allowed_negative_value=payload["allowNegativeValue"],
1720            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1721            counting_downward=payload["isCountingDownward"],
1722            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1723            progress_description=payload["progressDescription"],
1724            perks=payload["perks"],
1725            stats=payload["stats"],
1726            minimum_visibility=payload["minimumVisibilityThreshold"],
1727            allow_over_completion=payload["allowOvercompletion"],
1728            show_value_style=payload["showValueOnComplete"],
1729            display_only_objective=payload["isDisplayOnlyObjective"],
1730            complete_value_style=entity.ValueUIStyle(
1731                int(payload["completedValueStyle"])
1732            ),
1733            progress_value_style=entity.ValueUIStyle(
1734                int(payload["inProgressValueStyle"])
1735            ),
1736            ui_label=payload["uiLabel"],
1737            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1738        )

Deserialize a JSON payload of an objective entity information.

Parameters
Returns
def deserialize_activity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.Activity:
1766    def deserialize_activity(
1767        self,
1768        payload: typedefs.JSONObject,
1769        /,
1770    ) -> activity.Activity:
1771        period = time.clean_date(payload["period"])
1772        details = payload["activityDetails"]
1773        ref_id = int(details["referenceId"])
1774        instance_id = int(details["instanceId"])
1775        mode = enums.GameMode(details["mode"])
1776        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1777        is_private = details["isPrivate"]
1778        membership_type = enums.MembershipType(int(details["membershipType"]))
1779
1780        # Since we're using the same fields for post activity method
1781        # this check is required since post activity doesn't values values
1782        values = self._deserialize_activity_values(payload["values"])
1783
1784        return activity.Activity(
1785            net=self._net,
1786            hash=ref_id,
1787            instance_id=instance_id,
1788            mode=mode,
1789            modes=modes,
1790            is_private=is_private,
1791            membership_type=membership_type,
1792            occurred_at=period,
1793            values=values,
1794        )

Deserialize a JSON payload of an activity history information.

Parameters
Returns
def deserialize_activities( self, payload: dict[str, typing.Any]) -> Iterator[aiobungie.crates.activity.Activity]:
1796    def deserialize_activities(
1797        self, payload: typedefs.JSONObject
1798    ) -> iterators.Iterator[activity.Activity]:
1799        return iterators.Iterator(
1800            [
1801                self.deserialize_activity(activity_)
1802                for activity_ in payload["activities"]
1803            ]
1804        )

Deserialize a JSON payload of an array of activity history information.

Parameters
Returns
def deserialize_extended_weapon_values( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.ExtendedWeaponValues:
1806    def deserialize_extended_weapon_values(
1807        self, payload: typedefs.JSONObject
1808    ) -> activity.ExtendedWeaponValues:
1809        assists: typing.Optional[int] = None
1810        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1811            assists = raw_assists["basic"]["value"]
1812        assists_damage: typing.Optional[int] = None
1813
1814        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1815            assists_damage = raw_assists_damage["basic"]["value"]
1816
1817        return activity.ExtendedWeaponValues(
1818            reference_id=int(payload["referenceId"]),
1819            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1820            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1821                "value"
1822            ],
1823            assists=assists,
1824            assists_damage=assists_damage,
1825            precision_kills_percentage=(
1826                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1827                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1828                    "displayValue"
1829                ],
1830            ),
1831        )

Deserialize values of extended weapons JSON object.

Parameters
Returns
def deserialize_post_activity_player( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.PostActivityPlayer:
1854    def deserialize_post_activity_player(
1855        self, payload: typedefs.JSONObject, /
1856    ) -> activity.PostActivityPlayer:
1857        player = payload["player"]
1858
1859        class_hash: typedefs.NoneOr[int] = None
1860        if (class_hash := player.get("classHash")) is not None:
1861            class_hash = class_hash
1862
1863        race_hash: typedefs.NoneOr[int] = None
1864        if (race_hash := player.get("raceHash")) is not None:
1865            race_hash = race_hash
1866
1867        gender_hash: typedefs.NoneOr[int] = None
1868        if (gender_hash := player.get("genderHash")) is not None:
1869            gender_hash = gender_hash
1870
1871        character_class: undefined.UndefinedOr[str] = undefined.UNDEFINED
1872        if (
1873            character_class := player.get("characterClass")
1874        ) and not typedefs.is_unknown(character_class):
1875            character_class = character_class
1876
1877        character_level: typedefs.NoneOr[int] = None
1878        if (character_level := player.get("characterLevel")) is not None:
1879            character_level = character_level
1880
1881        return activity.PostActivityPlayer(
1882            standing=int(payload["standing"]),
1883            score=int(payload["score"]["basic"]["value"]),
1884            character_id=payload["characterId"],
1885            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1886            character_class=character_class,
1887            character_level=character_level,
1888            race_hash=race_hash,
1889            gender_hash=gender_hash,
1890            class_hash=class_hash,
1891            light_level=int(player["lightLevel"]),
1892            emblem_hash=int(player["emblemHash"]),
1893            values=self._deserialize_activity_values(payload["values"]),
1894            extended_values=self._deserialize_extended_values(payload["extended"]),
1895        )

Deserialize a JSON payload of a post activity player information.

Parameters
Returns
def deserialize_post_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.PostActivity:
1907    def deserialize_post_activity(
1908        self, payload: typedefs.JSONObject
1909    ) -> activity.PostActivity:
1910        period = time.clean_date(payload["period"])
1911        details = payload["activityDetails"]
1912        ref_id = int(details["referenceId"])
1913        instance_id = int(details["instanceId"])
1914        mode = enums.GameMode(details["mode"])
1915        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1916        is_private = details["isPrivate"]
1917        membership_type = enums.MembershipType(int(details["membershipType"]))
1918        return activity.PostActivity(
1919            net=self._net,
1920            hash=ref_id,
1921            membership_type=membership_type,
1922            instance_id=instance_id,
1923            mode=mode,
1924            modes=modes,
1925            is_private=is_private,
1926            occurred_at=period,
1927            starting_phase=int(payload["startingPhaseIndex"]),
1928            players=[
1929                self.deserialize_post_activity_player(player)
1930                for player in payload["entries"]
1931            ],
1932            teams=[
1933                self._deserialize_post_activity_team(team) for team in payload["teams"]
1934            ],
1935        )

Deserialize a JSON payload of a post activity information.

Parameters
Returns
def deserialize_aggregated_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AggregatedActivity:
1973    def deserialize_aggregated_activity(
1974        self, payload: typedefs.JSONObject
1975    ) -> activity.AggregatedActivity:
1976        return activity.AggregatedActivity(
1977            hash=int(payload["activityHash"]),
1978            values=self._deserialize_aggregated_activity_values(payload["values"]),
1979        )

Deserialize a JSON payload of an aggregated activity.

Parameters
Returns
def deserialize_aggregated_activities( self, payload: dict[str, typing.Any]) -> Iterator[aiobungie.crates.activity.AggregatedActivity]:
1981    def deserialize_aggregated_activities(
1982        self, payload: typedefs.JSONObject
1983    ) -> iterators.Iterator[activity.AggregatedActivity]:
1984        return iterators.Iterator(
1985            [
1986                self.deserialize_aggregated_activity(activity)
1987                for activity in payload["activities"]
1988            ]
1989        )

Deserialize a JSON payload of an array of aggregated activities.

Parameters
Returns
def deserialize_linked_profiles( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.LinkedProfile:
1991    def deserialize_linked_profiles(
1992        self, payload: typedefs.JSONObject
1993    ) -> profile.LinkedProfile:
1994        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
1995        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
1996        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
1997
1998        if raw_profile := payload.get("profiles"):
1999            for pfile in raw_profile:
2000                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2001
2002        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2003            for raw_error_pfile in raw_profiles_with_errors:
2004                if error_pfile := raw_error_pfile.get("infoCard"):
2005                    error_profiles_vec.append(
2006                        self.deserialize_destiny_membership(error_pfile)
2007                    )
2008
2009        return profile.LinkedProfile(
2010            bungie=bungie_user,
2011            profiles=profiles_vec,
2012            profiles_with_errors=error_profiles_vec,
2013        )

Deserialize a JSON payload of Bungie.net hard linked profile information.

Parameters
Returns
def deserialize_clan_banners( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
2015    def deserialize_clan_banners(
2016        self, payload: typedefs.JSONObject
2017    ) -> collections.Sequence[clans.ClanBanner]:
2018        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2019        if banners := payload.get("clanBannerDecals"):
2020            for k, v in banners.items():
2021                banner_obj = clans.ClanBanner(
2022                    id=int(k),
2023                    foreground=assets.Image(v["foregroundPath"]),
2024                    background=assets.Image(v["backgroundPath"]),
2025                )
2026                banners_seq.append(banner_obj)
2027        return banners_seq

Deserialize a JSON array of a clan banners information.

Parameters
Returns
def deserialize_public_milestone_content( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.MilestoneContent:
2029    def deserialize_public_milestone_content(
2030        self, payload: typedefs.JSONObject
2031    ) -> milestones.MilestoneContent:
2032        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2033        if raw_categories := payload.get("itemCategories"):
2034            for item in raw_categories:
2035                title = undefined.UNDEFINED
2036                if raw_title := item.get("title"):
2037                    if raw_title != typedefs.Unknown:
2038                        title = raw_title
2039                if raw_hashes := item.get("itemHashes"):
2040                    hashes: collections.Sequence[int] = raw_hashes
2041
2042                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2043
2044        about = undefined.UNDEFINED
2045        if (raw_about := payload["about"]) != typedefs.Unknown:
2046            about = raw_about
2047
2048        status = undefined.UNDEFINED
2049        if (raw_status := payload["status"]) != typedefs.Unknown:
2050            status = raw_status
2051
2052        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2053        if raw_tips := payload.get("tips"):
2054            for raw_tip in raw_tips:
2055                if raw_tip == typedefs.Unknown:
2056                    raw_tip = undefined.UNDEFINED
2057                tips.append(raw_tip)
2058
2059        return milestones.MilestoneContent(
2060            about=about, status=status, tips=tips, items=items_categoris
2061        )

Deserialize a JSON payload of milestone content information.

Parameters
Returns
def deserialize_friend( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.friends.Friend:
2063    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2064        name = undefined.UNDEFINED
2065        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2066            name = raw_name
2067
2068        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2069
2070        if raw_bungie_user := payload.get("bungieNetUser"):
2071            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2072
2073        return friends.Friend(
2074            net=self._net,
2075            id=int(payload["lastSeenAsMembershipId"]),
2076            name=name,
2077            code=payload.get("bungieGlobalDisplayNameCode"),
2078            relationship=enums.Relationship(payload["relationship"]),
2079            user=bungie_user,
2080            online_status=enums.Presence(payload["onlineStatus"]),
2081            online_title=payload["onlineTitle"],
2082            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2083        )

Deserialize a JSON payload of a Bungie friend information.

Parameters
Returns
def deserialize_friends( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
2085    def deserialize_friends(
2086        self, payload: typedefs.JSONObject
2087    ) -> collections.Sequence[friends.Friend]:
2088        mut_seq: typing.MutableSequence[friends.Friend] = []
2089        if raw_friends := payload.get("friends"):
2090            for friend in raw_friends:
2091                mut_seq.append(self.deserialize_friend(friend))
2092        return mut_seq

Deserialize a JSON sequence of Bungie friends information.

This is usually used to deserialize the incoming/outgoing friend requests.

Parameters
Returns
def deserialize_friend_requests( self, payload: dict[str, typing.Any]) -> aiobungie.crates.friends.FriendRequestView:
2094    def deserialize_friend_requests(
2095        self, payload: typedefs.JSONObject
2096    ) -> friends.FriendRequestView:
2097        incoming: typing.MutableSequence[friends.Friend] = []
2098        outgoing: typing.MutableSequence[friends.Friend] = []
2099
2100        if raw_incoming_requests := payload.get("incomingRequests"):
2101            for incoming_request in raw_incoming_requests:
2102                incoming.append(self.deserialize_friend(incoming_request))
2103
2104        if raw_outgoing_requests := payload.get("outgoingRequests"):
2105            for outgoing_request in raw_outgoing_requests:
2106                outgoing.append(self.deserialize_friend(outgoing_request))
2107
2108        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)

Deserialize a JSON sequence of Bungie friend requests information.

This is used for incoming/outgoing friend requests.

Parameters
Returns
def deserialize_fireteams( self, payload: dict[str, typing.Any]) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
2133    def deserialize_fireteams(
2134        self, payload: typedefs.JSONObject
2135    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2136        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2137
2138        result: list[typedefs.JSONObject]
2139        if not (result := payload["results"]):
2140            return None
2141        for elem in result:
2142            fireteams_.append(
2143                self._set_fireteam_fields(
2144                    elem, total_results=int(payload["totalResults"])
2145                )
2146            )
2147        return fireteams_

Deserialize a JSON sequence of Bungie fireteams information.

Parameters
Returns
def deserialize_fireteam_destiny_users( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamUser:
2149    def deserialize_fireteam_destiny_users(
2150        self, payload: typedefs.JSONObject
2151    ) -> fireteams.FireteamUser:
2152        destiny_obj = self.deserialize_destiny_membership(payload)
2153        # We could helpers.just return a DestinyMembership object but this is
2154        # missing the fireteam display name and id fields.
2155        return fireteams.FireteamUser(
2156            net=self._net,
2157            id=destiny_obj.id,
2158            code=destiny_obj.code,
2159            icon=destiny_obj.icon,
2160            types=destiny_obj.types,
2161            type=destiny_obj.type,
2162            is_public=destiny_obj.is_public,
2163            crossave_override=destiny_obj.crossave_override,
2164            name=destiny_obj.name,
2165            last_seen_name=destiny_obj.last_seen_name,
2166            fireteam_display_name=payload["FireteamDisplayName"],
2167            fireteam_membership_id=enums.MembershipType(
2168                payload["FireteamMembershipType"]
2169            ),
2170        )

Deserialize a JSON payload of Bungie fireteam destiny users information.

Parameters
Returns
def deserialize_fireteam_members( self, payload: dict[str, typing.Any], *, alternatives: bool = False) -> collections.abc.Sequence[aiobungie.crates.fireteams.FireteamMember]:
2172    def deserialize_fireteam_members(
2173        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2174    ) -> collections.Sequence[fireteams.FireteamMember]:
2175        members_: list[fireteams.FireteamMember] = []
2176        if members := payload.get("Members" if not alternatives else "Alternates"):
2177            for member in members:
2178                bungie_fields = self.deserialize_partial_bungie_user(member)
2179                members_fields = fireteams.FireteamMember(
2180                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2181                    has_microphone=member["hasMicrophone"],
2182                    character_id=int(member["characterId"]),
2183                    date_joined=time.clean_date(member["dateJoined"]),
2184                    last_platform_invite_date=time.clean_date(
2185                        member["lastPlatformInviteAttemptDate"]
2186                    ),
2187                    last_platform_invite_result=int(
2188                        member["lastPlatformInviteAttemptResult"]
2189                    ),
2190                    net=self._net,
2191                    name=bungie_fields.name,
2192                    id=bungie_fields.id,
2193                    icon=bungie_fields.icon,
2194                    is_public=bungie_fields.is_public,
2195                    crossave_override=bungie_fields.crossave_override,
2196                    types=bungie_fields.types,
2197                    type=bungie_fields.type,
2198                )
2199                members_.append(members_fields)
2200        return members_

Deserialize a JSON sequence of Bungie fireteam members information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • alternatives (bool): If set to True, Then it will deserialize the alternatives data in the payload. If not the it will just deserialize the members data.
Returns
def deserialize_available_fireteams( self, data: dict[str, typing.Any], *, no_results: bool = False) -> Union[aiobungie.crates.fireteams.AvailableFireteam, collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]]:
2203    def deserialize_available_fireteams(
2204        self,
2205        data: typedefs.JSONObject,
2206        *,
2207        no_results: bool = False,
2208    ) -> typing.Union[
2209        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2210    ]:
2211        fireteams_: list[fireteams.AvailableFireteam] = []
2212
2213        # This needs to be used outside the results
2214        # JSON key.
2215        if no_results:
2216            payload = data.copy()
2217
2218        if (results := payload.get("results")) is not None:
2219            for fireteam in results:
2220                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2221                fireteams_fields = fireteams.AvailableFireteam(
2222                    id=found_fireteams.id,
2223                    group_id=found_fireteams.group_id,
2224                    platform=found_fireteams.platform,
2225                    activity_type=found_fireteams.activity_type,
2226                    is_immediate=found_fireteams.is_immediate,
2227                    is_public=found_fireteams.is_public,
2228                    is_valid=found_fireteams.is_valid,
2229                    owner_id=found_fireteams.owner_id,
2230                    player_slot_count=found_fireteams.player_slot_count,
2231                    available_player_slots=found_fireteams.available_player_slots,
2232                    available_alternate_slots=found_fireteams.available_alternate_slots,
2233                    title=found_fireteams.title,
2234                    date_created=found_fireteams.date_created,
2235                    locale=found_fireteams.locale,
2236                    last_modified=found_fireteams.last_modified,
2237                    total_results=found_fireteams.total_results,
2238                    members=self.deserialize_fireteam_members(payload),
2239                    alternatives=self.deserialize_fireteam_members(
2240                        payload, alternatives=True
2241                    ),
2242                )
2243            if no_results:
2244                return fireteams_fields
2245            else:
2246                fireteams_.append(fireteams_fields)
2247        return fireteams_

Deserialize a JSON payload of a sequence of/fireteam information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • no_results (bool): Whether to deserialize the data from results in the payload or not.
Returns
  • typing.Union[aiobungie.crates.fireteams.AvailableFireteam, collections.Sequence[aiobungie.crates.fireteams.AvailableFireteam]] # noqa (E501): An available fireteam or a sequence of available fireteam.
def deserialize_fireteam_party( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamParty:
2249    def deserialize_fireteam_party(
2250        self, payload: typedefs.JSONObject
2251    ) -> fireteams.FireteamParty:
2252        last_destination_hash: typing.Optional[int] = None
2253        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2254            last_destination_hash = int(raw_dest_hash)
2255
2256        return fireteams.FireteamParty(
2257            members=[
2258                self._deserialize_fireteam_party_member(member)
2259                for member in payload["partyMembers"]
2260            ],
2261            activity=self._deserialize_fireteam_party_current_activity(
2262                payload["currentActivity"]
2263            ),
2264            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2265            last_destination_hash=last_destination_hash,
2266            tracking=payload["tracking"],
2267        )

Deserialize a JSON payload of profileTransitory component response.

Parameters
Returns
def deserialize_seasonal_artifact(self, payload: dict[str, typing.Any]) -> aiobungie.crates.season.Artifact:
2313    def deserialize_seasonal_artifact(
2314        self, payload: typedefs.JSONObject
2315    ) -> season.Artifact:
2316        if raw_artifact := payload.get("seasonalArtifact"):
2317            if points := raw_artifact.get("pointProgression"):
2318                points_prog = progressions.Progression(
2319                    hash=points["progressionHash"],
2320                    level=points["level"],
2321                    cap=points["levelCap"],
2322                    daily_limit=points["dailyLimit"],
2323                    weekly_limit=points["weeklyLimit"],
2324                    current_progress=points["currentProgress"],
2325                    daily_progress=points["dailyProgress"],
2326                    needed=points["progressToNextLevel"],
2327                    next_level=points["nextLevelAt"],
2328                )
2329
2330            if bonus := raw_artifact.get("powerBonusProgression"):
2331                power_bonus_prog = progressions.Progression(
2332                    hash=bonus["progressionHash"],
2333                    level=bonus["level"],
2334                    cap=bonus["levelCap"],
2335                    daily_limit=bonus["dailyLimit"],
2336                    weekly_limit=bonus["weeklyLimit"],
2337                    current_progress=bonus["currentProgress"],
2338                    daily_progress=bonus["dailyProgress"],
2339                    needed=bonus["progressToNextLevel"],
2340                    next_level=bonus["nextLevelAt"],
2341                )
2342            artifact = season.Artifact(
2343                hash=raw_artifact["artifactHash"],
2344                power_bonus=raw_artifact["powerBonus"],
2345                acquired_points=raw_artifact["pointsAcquired"],
2346                bonus=power_bonus_prog,
2347                points=points_prog,
2348            )
2349        return artifact

Deserialize a JSON payload of a Destiny 2 seasonal artifact information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_profile_progression( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileProgression:
2351    def deserialize_profile_progression(
2352        self, payload: typedefs.JSONObject
2353    ) -> profile.ProfileProgression:
2354        return profile.ProfileProgression(
2355            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2356            checklist={
2357                int(check_id): checklists
2358                for check_id, checklists in payload["data"]["checklists"].items()
2359            },
2360        )

Deserialize a JSON payload of a profile progression component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_instanced_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemInstance:
2362    def deserialize_instanced_item(
2363        self, payload: typedefs.JSONObject
2364    ) -> items.ItemInstance:
2365        damage_type_hash: typing.Optional[int] = None
2366        if raw_damagetype_hash := payload.get("damageTypeHash"):
2367            damage_type_hash = int(raw_damagetype_hash)
2368
2369        required_hashes: typing.Optional[collections.Collection[int]] = None
2370        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2371            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2372
2373        breaker_type: typing.Optional[items.ItemBreakerType] = None
2374        if raw_break_type := payload.get("breakerType"):
2375            breaker_type = items.ItemBreakerType(int(raw_break_type))
2376
2377        breaker_type_hash: typing.Optional[int] = None
2378        if raw_break_type_hash := payload.get("breakerTypeHash"):
2379            breaker_type_hash = int(raw_break_type_hash)
2380
2381        energy: typing.Optional[items.ItemEnergy] = None
2382        if raw_energy := payload.get("energy"):
2383            energy = self.deserialize_item_energy(raw_energy)
2384
2385        primary_stats = None
2386        if raw_primary_stats := payload.get("primaryStat"):
2387            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2388
2389        return items.ItemInstance(
2390            damage_type=enums.DamageType(int(payload["damageType"])),
2391            damage_type_hash=damage_type_hash,
2392            primary_stat=primary_stats,
2393            item_level=int(payload["itemLevel"]),
2394            quality=int(payload["quality"]),
2395            is_equipped=payload["isEquipped"],
2396            can_equip=payload["canEquip"],
2397            equip_required_level=int(payload["equipRequiredLevel"]),
2398            required_equip_unlock_hashes=required_hashes,
2399            cant_equip_reason=int(payload["cannotEquipReason"]),
2400            breaker_type=breaker_type,
2401            breaker_type_hash=breaker_type_hash,
2402            energy=energy,
2403        )

Deserialize a JSON object into an instanced item.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_item_energy( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemEnergy:
2405    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2406        energy_hash: typing.Optional[int] = None
2407        if raw_energy_hash := payload.get("energyTypeHash"):
2408            energy_hash = int(raw_energy_hash)
2409
2410        return items.ItemEnergy(
2411            hash=energy_hash,
2412            type=items.ItemEnergyType(int(payload["energyType"])),
2413            capacity=int(payload["energyCapacity"]),
2414            used_energy=int(payload["energyUsed"]),
2415            unused_energy=int(payload["energyUnused"]),
2416        )
def deserialize_item_perk(self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemPerk:
2418    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2419        perk_hash: typing.Optional[int] = None
2420        if raw_perk_hash := payload.get("perkHash"):
2421            perk_hash = int(raw_perk_hash)
2422
2423        return items.ItemPerk(
2424            hash=perk_hash,
2425            icon=assets.Image(payload["iconPath"]),
2426            is_active=payload["isActive"],
2427            is_visible=payload["visible"],
2428        )
def deserialize_item_socket( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemSocket:
2430    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2431        plug_hash: typing.Optional[int] = None
2432        if raw_plug_hash := payload.get("plugHash"):
2433            plug_hash = int(raw_plug_hash)
2434
2435        enable_fail_indexes: typing.Optional[list[int]] = None
2436        if raw_indexes := payload.get("enableFailIndexes"):
2437            enable_fail_indexes = [int(index) for index in raw_indexes]
2438
2439        return items.ItemSocket(
2440            plug_hash=plug_hash,
2441            is_enabled=payload["isEnabled"],
2442            enable_fail_indexes=enable_fail_indexes,
2443            is_visible=payload.get("visible"),
2444        )
def deserialize_item_stats_view( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemStatsView:
2446    def deserialize_item_stats_view(
2447        self, payload: typedefs.JSONObject
2448    ) -> items.ItemStatsView:
2449        return items.ItemStatsView(
2450            stat_hash=payload.get("statHash"), value=payload.get("value")
2451        )
def deserialize_plug_item_state( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.PlugItemState:
2453    def deserialize_plug_item_state(
2454        self, payload: typedefs.JSONObject
2455    ) -> items.PlugItemState:
2456        item_hash: typing.Optional[int] = None
2457        if raw_item_hash := payload.get("plugItemHash"):
2458            item_hash = int(raw_item_hash)
2459
2460        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2461        if raw_fail_indexes := payload.get("insertFailIndexes"):
2462            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2463
2464        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2465        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2466            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2467
2468        return items.PlugItemState(
2469            item_hash=item_hash,
2470            insert_fail_indexes=insert_fail_indexes,
2471            enable_fail_indexes=enable_fail_indexes,
2472            is_enabled=payload["enabled"],
2473            can_insert=payload["canInsert"],
2474        )
@typing.final
class FireteamActivity(builtins.int, aiobungie.Enum):
 67@typing.final
 68class FireteamActivity(int, enums.Enum):
 69    """An enum for the fireteam activities."""
 70
 71    ALL = 0
 72    CRUCIBLE = 2
 73    TRIALS_OF_OSIRIS = 3
 74    NIGHTFALL = 4
 75    ANY = 5
 76    GAMBIT = 6
 77    BLIND_WELL = 7
 78    NIGHTMARE_HUNTS = 12
 79    ALTARS_OF_SORROWS = 14
 80    DUNGEON = 15
 81    RAID_LW = 20
 82    RAID_GOS = 21
 83    RAID_DSC = 22
 84    EXO_CHALLENGE = 23
 85    S12_WRATHBORN = 24
 86    EMPIRE_HUNTS = 25
 87    S13_BATTLEGROUNDS = 26
 88    EXOTIC_QUEST = 27
 89    RAID_VOG = 28
 90    S14_EXPUNGE = 30
 91    S15_ASTRAL_ALIGNMENT = 31
 92    S15_SHATTERED_RELAM = 32
 93    SHATTERED_THRONE = 33
 94    PROPHECY = 34
 95    PIT_OF_HERESY = 35
 96    DOE = 36
 97    """Dares of Eternity."""
 98    DUNGEON_GOA = 37
 99    """Grasp of Avarice."""
100    VOW_OF_THE_DISCPILE = 38
101    CAMPAIGN = 39
102    WELLSPRING = 40
103    S16_BATTLEGROUNDS = 41
104    S17_NIGHTMARE_CONTAINMENT = 44
105    S17_SEVER = 45

An enum for the fireteam activities.

CRUCIBLE = <FireteamActivity.CRUCIBLE: 2>
TRIALS_OF_OSIRIS = <FireteamActivity.TRIALS_OF_OSIRIS: 3>
NIGHTFALL = <FireteamActivity.NIGHTFALL: 4>
GAMBIT = <FireteamActivity.GAMBIT: 6>
BLIND_WELL = <FireteamActivity.BLIND_WELL: 7>
NIGHTMARE_HUNTS = <FireteamActivity.NIGHTMARE_HUNTS: 12>
ALTARS_OF_SORROWS = <FireteamActivity.ALTARS_OF_SORROWS: 14>
DUNGEON = <FireteamActivity.DUNGEON: 15>
RAID_LW = <FireteamActivity.RAID_LW: 20>
RAID_GOS = <FireteamActivity.RAID_GOS: 21>
RAID_DSC = <FireteamActivity.RAID_DSC: 22>
EXO_CHALLENGE = <FireteamActivity.EXO_CHALLENGE: 23>
S12_WRATHBORN = <FireteamActivity.S12_WRATHBORN: 24>
EMPIRE_HUNTS = <FireteamActivity.EMPIRE_HUNTS: 25>
S13_BATTLEGROUNDS = <FireteamActivity.S13_BATTLEGROUNDS: 26>
EXOTIC_QUEST = <FireteamActivity.EXOTIC_QUEST: 27>
RAID_VOG = <FireteamActivity.RAID_VOG: 28>
S14_EXPUNGE = <FireteamActivity.S14_EXPUNGE: 30>
S15_ASTRAL_ALIGNMENT = <FireteamActivity.S15_ASTRAL_ALIGNMENT: 31>
S15_SHATTERED_RELAM = <FireteamActivity.S15_SHATTERED_RELAM: 32>
SHATTERED_THRONE = <FireteamActivity.SHATTERED_THRONE: 33>
PROPHECY = <FireteamActivity.PROPHECY: 34>
PIT_OF_HERESY = <FireteamActivity.PIT_OF_HERESY: 35>
DOE = <FireteamActivity.DOE: 36>

Dares of Eternity.

DUNGEON_GOA = <FireteamActivity.DUNGEON_GOA: 37>

Grasp of Avarice.

VOW_OF_THE_DISCPILE = <FireteamActivity.VOW_OF_THE_DISCPILE: 38>
CAMPAIGN = <FireteamActivity.CAMPAIGN: 39>
WELLSPRING = <FireteamActivity.WELLSPRING: 40>
S16_BATTLEGROUNDS = <FireteamActivity.S16_BATTLEGROUNDS: 41>
S17_NIGHTMARE_CONTAINMENT = <FireteamActivity.S17_NIGHTMARE_CONTAINMENT: 44>
S17_SEVER = <FireteamActivity.S17_SEVER: 45>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamDate(builtins.int, aiobungie.Enum):
131@typing.final
132class FireteamDate(int, enums.Enum):
133    """An enum for fireteam date ranges."""
134
135    ALL = 0
136    NOW = 1
137    TODAY = 2
138    TWO_DAYS = 3
139    THIS_WEEK = 4

An enum for fireteam date ranges.

ALL = <FireteamDate.ALL: 0>
NOW = <FireteamDate.NOW: 1>
TODAY = <FireteamDate.TODAY: 2>
TWO_DAYS = <FireteamDate.TWO_DAYS: 3>
THIS_WEEK = <FireteamDate.THIS_WEEK: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamLanguage(builtins.str, aiobungie.Enum):
108@typing.final
109class FireteamLanguage(str, enums.Enum):
110    """An enum for fireteams languages filters."""
111
112    ALL = ""
113    ENGLISH = "en"
114    FRENCH = "fr"
115    ESPANOL = "es"
116    DEUTSCH = "de"
117    ITALIAN = "it"
118    JAPANESE = "ja"
119    PORTUGUESE = "pt-br"
120    RUSSIAN = "ru"
121    POLISH = "pl"
122    KOREAN = "ko"
123    # ? China
124    ZH_CHT = "zh-cht"
125    ZH_CHS = "zh-chs"
126
127    def __str__(self) -> str:
128        return str(self.value)

An enum for fireteams languages filters.

ENGLISH = <FireteamLanguage.ENGLISH: en>
FRENCH = <FireteamLanguage.FRENCH: fr>
ESPANOL = <FireteamLanguage.ESPANOL: es>
DEUTSCH = <FireteamLanguage.DEUTSCH: de>
ITALIAN = <FireteamLanguage.ITALIAN: it>
JAPANESE = <FireteamLanguage.JAPANESE: ja>
PORTUGUESE = <FireteamLanguage.PORTUGUESE: pt-br>
RUSSIAN = <FireteamLanguage.RUSSIAN: ru>
POLISH = <FireteamLanguage.POLISH: pl>
KOREAN = <FireteamLanguage.KOREAN: ko>
ZH_CHT = <FireteamLanguage.ZH_CHT: zh-cht>
ZH_CHS = <FireteamLanguage.ZH_CHS: zh-chs>
Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@typing.final
class FireteamPlatform(builtins.int, aiobungie.Enum):
54@typing.final
55class FireteamPlatform(int, enums.Enum):
56    """An enum for fireteam related to bungie fireteams.
57    This is different from the normal `aiobungie.MembershipType`.
58    """
59
60    ANY = 0
61    PSN_NETWORK = 1
62    XBOX_LIVE = 2
63    STEAM = 4
64    STADIA = 5

An enum for fireteam related to bungie fireteams. This is different from the normal aiobungie.MembershipType.

PSN_NETWORK = <FireteamPlatform.PSN_NETWORK: 1>
XBOX_LIVE = <FireteamPlatform.XBOX_LIVE: 2>
STEAM = <FireteamPlatform.STEAM: 4>
STADIA = <FireteamPlatform.STADIA: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Flag(enum.Flag):
 91class Flag(__enum.Flag):
 92    """Builtin Python enum flag with extra handlings."""
 93
 94    # Needs to type this here for mypy
 95    _value_: int
 96
 97    @property
 98    def name(self) -> str:  # type: ignore[override]
 99        if self._name_ is None:
100            self._name_ = f"UNKNOWN {self._value_}"
101
102        return self._name_
103
104    @property
105    def value(self) -> int:  # type: ignore[override]
106        return self._value_
107
108    def __str__(self) -> str:
109        return self.name
110
111    def __repr__(self) -> str:
112        return f"<{type(self).__name__}.{self.name}: {self._value_!s}>"
113
114    def __int__(self) -> int:
115        return int(self.value)
116
117    def __or__(self, other: typing.Union[Flag, int]) -> Flag:
118        return self.__class__(self._value_ | int(other))
119
120    def __xor__(self, other: typing.Union[Flag, int]) -> Flag:
121        return self.__class__(self._value_ ^ int(other))
122
123    def __and__(self, other: typing.Union[Flag, int]) -> Flag:
124        return self.__class__(other & int(other))
125
126    def __invert__(self) -> Flag:
127        return self.__class__(~self._value_)
128
129    def __contains__(self, other: typing.Union[Flag, int]) -> bool:
130        return self.value & int(other) == int(other)

Builtin Python enum flag with extra handlings.

name: str

The name of the Enum member.

value: int

The value of the Enum member.

@attrs.define(auto_exc=True)
class Forbidden(aiobungie.HTTPException):
136@attrs.define(auto_exc=True)
137class Forbidden(HTTPException):
138    """Exception that's raised for when status code 403 occurs."""
139
140    http_status: http.HTTPStatus = attrs.field(
141        default=http.HTTPStatus.FORBIDDEN, init=False
142    )

Exception that's raised for when status code 403 occurs.

Forbidden( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Forbidden.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class GameMode(builtins.int, aiobungie.Enum):
259@typing.final
260class GameMode(int, Enum):
261    """An Enum for all available gamemodes in Destiny 2."""
262
263    NONE = 0
264    STORY = 2
265    STRIKE = 3
266    RAID = 4
267    ALLPVP = 5
268    PATROL = 6
269    ALLPVE = 7
270    RESERVED9 = 9
271    CONTROL = 10
272    RESERVED11 = 11
273    CLASH = 12
274    RESERVED13 = 13
275    CRIMSONDOUBLES = 15
276    NIGHTFALL = 16
277    HEROICNIGHTFALL = 17
278    ALLSTRIKES = 18
279    IRONBANNER = 19
280    RESERVED20 = 20
281    RESERVED21 = 21
282    RESERVED22 = 22
283    RESERVED24 = 24
284    ALLMAYHEM = 25
285    RESERVED26 = 26
286    RESERVED27 = 27
287    RESERVED28 = 28
288    RESERVED29 = 29
289    RESERVED30 = 30
290    SUPREMACY = 31
291    PRIVATEMATCHESALL = 32
292    SURVIVAL = 37
293    COUNTDOWN = 38
294    TRIALSOFTHENINE = 39
295    SOCIAL = 40
296    TRIALSCOUNTDOWN = 41
297    TRIALSSURVIVAL = 42
298    IRONBANNERCONTROL = 43
299    IRONBANNERCLASH = 44
300    IRONBANNERSUPREMACY = 45
301    SCOREDNIGHTFALL = 46
302    SCOREDHEROICNIGHTFALL = 47
303    RUMBLE = 48
304    ALLDOUBLES = 49
305    DOUBLES = 50
306    PRIVATEMATCHESCLASH = 51
307    PRIVATEMATCHESCONTROL = 52
308    PRIVATEMATCHESSUPREMACY = 53
309    PRIVATEMATCHESCOUNTDOWN = 54
310    PRIVATEMATCHESSURVIVAL = 55
311    PRIVATEMATCHESMAYHEM = 56
312    PRIVATEMATCHESRUMBLE = 57
313    HEROICADVENTURE = 58
314    SHOWDOWN = 59
315    LOCKDOWN = 60
316    SCORCHED = 61
317    SCORCHEDTEAM = 62
318    GAMBIT = 63
319    ALLPVECOMPETITIVE = 64
320    BREAKTHROUGH = 65
321    BLACKARMORYRUN = 66
322    SALVAGE = 67
323    IRONBANNERSALVAGE = 68
324    PVPCOMPETITIVE = 69
325    PVPQUICKPLAY = 70
326    CLASHQUICKPLAY = 71
327    CLASHCOMPETITIVE = 72
328    CONTROLQUICKPLAY = 73
329    CONTROLCOMPETITIVE = 74
330    GAMBITPRIME = 75
331    RECKONING = 76
332    MENAGERIE = 77
333    VEXOFFENSIVE = 78
334    NIGHTMAREHUNT = 79
335    ELIMINATION = 80
336    MOMENTUM = 81
337    DUNGEON = 82
338    SUNDIAL = 83
339    TRIALS_OF_OSIRIS = 84
340    DARES = 85
341    OFFENSIVE = 86
342    LOSTSECTOR = 87
343    RIFT = 88
344    ZONECONTROL = 89
345    IRONBANNERRIFT = 90

An Enum for all available gamemodes in Destiny 2.

NONE = <GameMode.NONE: 0>
STORY = <GameMode.STORY: 2>
STRIKE = <GameMode.STRIKE: 3>
RAID = <GameMode.RAID: 4>
ALLPVP = <GameMode.ALLPVP: 5>
PATROL = <GameMode.PATROL: 6>
ALLPVE = <GameMode.ALLPVE: 7>
RESERVED9 = <GameMode.RESERVED9: 9>
CONTROL = <GameMode.CONTROL: 10>
RESERVED11 = <GameMode.RESERVED11: 11>
CLASH = <GameMode.CLASH: 12>
RESERVED13 = <GameMode.RESERVED13: 13>
CRIMSONDOUBLES = <GameMode.CRIMSONDOUBLES: 15>
NIGHTFALL = <GameMode.NIGHTFALL: 16>
HEROICNIGHTFALL = <GameMode.HEROICNIGHTFALL: 17>
ALLSTRIKES = <GameMode.ALLSTRIKES: 18>
IRONBANNER = <GameMode.IRONBANNER: 19>
RESERVED20 = <GameMode.RESERVED20: 20>
RESERVED21 = <GameMode.RESERVED21: 21>
RESERVED22 = <GameMode.RESERVED22: 22>
RESERVED24 = <GameMode.RESERVED24: 24>
ALLMAYHEM = <GameMode.ALLMAYHEM: 25>
RESERVED26 = <GameMode.RESERVED26: 26>
RESERVED27 = <GameMode.RESERVED27: 27>
RESERVED28 = <GameMode.RESERVED28: 28>
RESERVED29 = <GameMode.RESERVED29: 29>
RESERVED30 = <GameMode.RESERVED30: 30>
SUPREMACY = <GameMode.SUPREMACY: 31>
PRIVATEMATCHESALL = <GameMode.PRIVATEMATCHESALL: 32>
SURVIVAL = <GameMode.SURVIVAL: 37>
COUNTDOWN = <GameMode.COUNTDOWN: 38>
TRIALSOFTHENINE = <GameMode.TRIALSOFTHENINE: 39>
SOCIAL = <GameMode.SOCIAL: 40>
TRIALSCOUNTDOWN = <GameMode.TRIALSCOUNTDOWN: 41>
TRIALSSURVIVAL = <GameMode.TRIALSSURVIVAL: 42>
IRONBANNERCONTROL = <GameMode.IRONBANNERCONTROL: 43>
IRONBANNERCLASH = <GameMode.IRONBANNERCLASH: 44>
IRONBANNERSUPREMACY = <GameMode.IRONBANNERSUPREMACY: 45>
SCOREDNIGHTFALL = <GameMode.SCOREDNIGHTFALL: 46>
SCOREDHEROICNIGHTFALL = <GameMode.SCOREDHEROICNIGHTFALL: 47>
RUMBLE = <GameMode.RUMBLE: 48>
ALLDOUBLES = <GameMode.ALLDOUBLES: 49>
DOUBLES = <GameMode.DOUBLES: 50>
PRIVATEMATCHESCLASH = <GameMode.PRIVATEMATCHESCLASH: 51>
PRIVATEMATCHESCONTROL = <GameMode.PRIVATEMATCHESCONTROL: 52>
PRIVATEMATCHESSUPREMACY = <GameMode.PRIVATEMATCHESSUPREMACY: 53>
PRIVATEMATCHESCOUNTDOWN = <GameMode.PRIVATEMATCHESCOUNTDOWN: 54>
PRIVATEMATCHESSURVIVAL = <GameMode.PRIVATEMATCHESSURVIVAL: 55>
PRIVATEMATCHESMAYHEM = <GameMode.PRIVATEMATCHESMAYHEM: 56>
PRIVATEMATCHESRUMBLE = <GameMode.PRIVATEMATCHESRUMBLE: 57>
HEROICADVENTURE = <GameMode.HEROICADVENTURE: 58>
SHOWDOWN = <GameMode.SHOWDOWN: 59>
LOCKDOWN = <GameMode.LOCKDOWN: 60>
SCORCHED = <GameMode.SCORCHED: 61>
SCORCHEDTEAM = <GameMode.SCORCHEDTEAM: 62>
GAMBIT = <GameMode.GAMBIT: 63>
ALLPVECOMPETITIVE = <GameMode.ALLPVECOMPETITIVE: 64>
BREAKTHROUGH = <GameMode.BREAKTHROUGH: 65>
BLACKARMORYRUN = <GameMode.BLACKARMORYRUN: 66>
SALVAGE = <GameMode.SALVAGE: 67>
IRONBANNERSALVAGE = <GameMode.IRONBANNERSALVAGE: 68>
PVPCOMPETITIVE = <GameMode.PVPCOMPETITIVE: 69>
PVPQUICKPLAY = <GameMode.PVPQUICKPLAY: 70>
CLASHQUICKPLAY = <GameMode.CLASHQUICKPLAY: 71>
CLASHCOMPETITIVE = <GameMode.CLASHCOMPETITIVE: 72>
CONTROLQUICKPLAY = <GameMode.CONTROLQUICKPLAY: 73>
CONTROLCOMPETITIVE = <GameMode.CONTROLCOMPETITIVE: 74>
GAMBITPRIME = <GameMode.GAMBITPRIME: 75>
RECKONING = <GameMode.RECKONING: 76>
MENAGERIE = <GameMode.MENAGERIE: 77>
VEXOFFENSIVE = <GameMode.VEXOFFENSIVE: 78>
NIGHTMAREHUNT = <GameMode.NIGHTMAREHUNT: 79>
ELIMINATION = <GameMode.ELIMINATION: 80>
MOMENTUM = <GameMode.MOMENTUM: 81>
DUNGEON = <GameMode.DUNGEON: 82>
SUNDIAL = <GameMode.SUNDIAL: 83>
TRIALS_OF_OSIRIS = <GameMode.TRIALS_OF_OSIRIS: 84>
DARES = <GameMode.DARES: 85>
OFFENSIVE = <GameMode.OFFENSIVE: 86>
LOSTSECTOR = <GameMode.LOSTSECTOR: 87>
RIFT = <GameMode.RIFT: 88>
ZONECONTROL = <GameMode.ZONECONTROL: 89>
IRONBANNERRIFT = <GameMode.IRONBANNERRIFT: 90>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GatingScope(builtins.int, aiobungie.Enum):
58@typing.final
59class GatingScope(int, enums.Enum):
60    """An enum represents restrictive type of gating that is being performed by an entity.
61
62    This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity
63    applies to everyone equally, or to their specific Profile or Character states.
64    """
65
66    NONE = 0
67    GLOBAL = 1
68    CLAN = 2
69    PROFILE = 3
70    CHARACTER = 4
71    ITEM = 5
72    ASSUMED_WORST_CASE = 6

An enum represents restrictive type of gating that is being performed by an entity.

This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity applies to everyone equally, or to their specific Profile or Character states.

NONE = <GatingScope.NONE: 0>
GLOBAL = <GatingScope.GLOBAL: 1>
CLAN = <GatingScope.CLAN: 2>
PROFILE = <GatingScope.PROFILE: 3>
CHARACTER = <GatingScope.CHARACTER: 4>
ITEM = <GatingScope.ITEM: 5>
ASSUMED_WORST_CASE = <GatingScope.ASSUMED_WORST_CASE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Gender(builtins.int, aiobungie.Enum):
476@typing.final
477class Gender(int, Enum):
478    """An Enum for Destiny Genders."""
479
480    MALE = 0
481    FEMALE = 1
482    UNKNOWN = 2

An Enum for Destiny Genders.

MALE = <Gender.MALE: 0>
FEMALE = <Gender.FEMALE: 1>
UNKNOWN = <Gender.UNKNOWN: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GroupType(builtins.int, aiobungie.Enum):
645@typing.final
646class GroupType(int, Enum):
647    """An enums for the known bungie group types."""
648
649    GENERAL = 0
650    CLAN = 1

An enums for the known bungie group types.

GENERAL = <GroupType.GENERAL: 0>
CLAN = <GroupType.CLAN: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class HTTPError(aiobungie.AiobungieError):
78@attrs.define(auto_exc=True)
79class HTTPError(AiobungieError):
80    """Base HTTP request errors exception."""
81
82    message: str
83    """The error message."""
84
85    http_status: http.HTTPStatus
86    """The response status."""

Base HTTP request errors exception.

HTTPError(message: str, http_status: http.HTTPStatus)
2def __init__(self, message, http_status):
3    self.message = message
4    self.http_status = http_status
5    BaseException.__init__(self, self.message,self.http_status)

Method generated by attrs for class HTTPError.

message: str

The error message.

http_status: http.HTTPStatus

The response status.

Inherited Members
builtins.BaseException
with_traceback
args
@attrs.define(auto_exc=True, kw_only=True)
class HTTPException(aiobungie.HTTPError):
 89@attrs.define(auto_exc=True, kw_only=True)
 90class HTTPException(HTTPError):
 91    """An in-depth HTTP exception that's raised with more information."""
 92
 93    error_code: int
 94    """The returned Bungie error status code."""
 95
 96    http_status: http.HTTPStatus
 97    """The request response http status."""
 98
 99    throttle_seconds: int
100    """The Bungie response throttle seconds."""
101
102    url: typing.Optional[typedefs.StrOrURL]
103    """The URL/endpoint caused this error."""
104
105    body: typing.Any
106    """The response body."""
107
108    headers: multidict.CIMultiDictProxy[str]
109    """The response headers."""
110
111    message: str
112    """A Bungie human readable message describes the cause of the error."""
113
114    error_status: str
115    """A Bungie short error status describes the cause of the error."""
116
117    message_data: dict[str, str]
118    """A dict of string key, value that includes each cause of the error
119    to a message describes information about that error.
120    """
121
122    def __str__(self) -> str:
123        if self.message:
124            message_body = self.message
125
126        if self.error_status:
127            error_status_body = self.error_status
128
129        return (
130            f"{self.http_status.name.replace('_', '').title()} {self.http_status.value}: "
131            f"Error status: {error_status_body}, Error message: {message_body} from {self.url} "
132            f"{str(self.body)}"
133        )

An in-depth HTTP exception that's raised with more information.

HTTPException( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class HTTPException.

error_code: int

The returned Bungie error status code.

http_status: http.HTTPStatus

The request response http status.

throttle_seconds: int

The Bungie response throttle seconds.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

message: str

A Bungie human readable message describes the cause of the error.

error_status: str

A Bungie short error status describes the cause of the error.

message_data: dict[str, str]

A dict of string key, value that includes each cause of the error to a message describes information about that error.

Inherited Members
builtins.BaseException
with_traceback
args
class Image:
 71class Image:
 72    """Representation of an image/avatar/picture at Bungie.
 73
 74    Example
 75    -------
 76    ```py
 77    from aiobungie import Image
 78    img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
 79    print(img)
 80    # https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg
 81
 82    # Stream the image.
 83    async for chunk in img:
 84        # Byte chunks of the image.
 85        print(chunk)
 86
 87    # Save the image to a file.
 88    await img.save("file_name", "/my/path/to/save/to", "jpeg")
 89    ```
 90
 91    Parameters
 92    ----------
 93    path : `str | None`
 94        The path to the image. If `None`, the default missing image path will be used.
 95    """
 96
 97    __slots__ = ("_path",)
 98
 99    def __init__(self, path: typing.Optional[str] = None) -> None:
100        self._path = path
101
102    @property
103    def is_missing(self) -> bool:
104        return not self._path
105
106    @property
107    def url(self) -> str:
108        """The URL to the image."""
109        return self.create_url()
110
111    @staticmethod
112    def missing_path() -> str:
113        """Returns the path to the missing Bungie image."""
114        return "img/misc/missing_icon_d2.png"
115
116    def create_url(self) -> str:
117        """Creates a full URL to the image path.
118
119        Returns
120        -------
121        str
122            The URL to the image.
123        """
124        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"
125
126    async def save(
127        self,
128        file_name: str,
129        path: typing.Union[pathlib.Path, str],
130        /,
131        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
132    ) -> None:
133        """Saves the image to a file.
134
135        Parameters
136        ----------
137        file_name : `str`
138            A name for the file to save the image to.
139        path : `pathlib.Path | str`
140            A path tp save the image to.
141
142        Other Parameters
143        ----------------
144        mime_type : `MimeType | str`
145            Optional MIME type of the image.
146
147        Raises
148        ------
149        `FileNotFoundError`
150            If the path provided does not exist.
151        `RuntimeError`
152            If the image could not be saved.
153        `PermissionError`
154            If the path provided is not writable or does not have write permissions.
155        """
156        if isinstance(path, pathlib.Path) and not path.exists():
157            raise FileNotFoundError(f"File does not exist: {path!r}")
158
159        if self.is_missing:
160            return
161
162        mimetype = mime_type or MimeType.PNG
163        path = pathlib.Path(path)
164
165        loop = helpers.get_or_make_loop()
166        pool = concurrent.futures.ThreadPoolExecutor()
167
168        try:
169            with pool:
170                await loop.run_in_executor(
171                    pool, _write, path, file_name, mimetype, await self.read()
172                )
173                _LOGGER.info("Saved image to %s", file_name)
174
175        except asyncio.CancelledError:
176            pass
177
178        except Exception as err:
179            raise RuntimeError("Encountered an error while saving image.") from err
180
181    async def read(self) -> bytes:
182        """Read this image bytes.
183
184        Returns
185        -------
186        `bytes`
187            The bytes of this image.
188        """
189        client_session = aiohttp.ClientSession()
190
191        try:
192            await client_session.__aenter__()
193            response = await client_session.get(self.create_url())
194
195            if 300 >= response.status >= 200:
196                reader = await response.read()
197
198        except Exception as exc:
199            raise RuntimeError(f"Failed to read image: {exc}") from None
200        finally:
201            await client_session.__aexit__(None, None, None)
202        return reader
203
204    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
205        """Iterates over the image bytes lazily.
206
207        Example
208        -------
209        import aiobungie
210
211        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
212        async for chunk in resource.iter():
213            print(chunk)
214
215        Returns
216        -------
217        `collections.AsyncGenerator[bytes, None]`
218            An async generator of the image bytes.
219        """
220
221        async for chunk in self:
222            yield chunk
223
224    def __repr__(self) -> str:
225        return f"Image(url={self.create_url()})"
226
227    def __str__(self) -> str:
228        return self.create_url()
229
230    def __aiter__(self) -> Image:
231        return self
232
233    async def __anext__(self) -> bytes:
234        return await self.read()
235
236    def __await__(self) -> collections.Generator[None, None, bytes]:
237        return self.__anext__().__await__()

Representation of an image/avatar/picture at Bungie.

Example
from aiobungie import Image
img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
print(img)
# https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg

# Stream the image.
async for chunk in img:
    # Byte chunks of the image.
    print(chunk)

# Save the image to a file.
await img.save("file_name", "/my/path/to/save/to", "jpeg")
Parameters
  • path (str | None): The path to the image. If None, the default missing image path will be used.
Image(path: Optional[str] = None)
 99    def __init__(self, path: typing.Optional[str] = None) -> None:
100        self._path = path
is_missing: bool
url: str

The URL to the image.

@staticmethod
def missing_path() -> str:
111    @staticmethod
112    def missing_path() -> str:
113        """Returns the path to the missing Bungie image."""
114        return "img/misc/missing_icon_d2.png"

Returns the path to the missing Bungie image.

def create_url(self) -> str:
116    def create_url(self) -> str:
117        """Creates a full URL to the image path.
118
119        Returns
120        -------
121        str
122            The URL to the image.
123        """
124        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"

Creates a full URL to the image path.

Returns
  • str: The URL to the image.
async def save( self, file_name: str, path: Union[pathlib.Path, str], /, mime_type: Union[aiobungie.internal.assets.MimeType, str, NoneType] = None) -> None:
126    async def save(
127        self,
128        file_name: str,
129        path: typing.Union[pathlib.Path, str],
130        /,
131        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
132    ) -> None:
133        """Saves the image to a file.
134
135        Parameters
136        ----------
137        file_name : `str`
138            A name for the file to save the image to.
139        path : `pathlib.Path | str`
140            A path tp save the image to.
141
142        Other Parameters
143        ----------------
144        mime_type : `MimeType | str`
145            Optional MIME type of the image.
146
147        Raises
148        ------
149        `FileNotFoundError`
150            If the path provided does not exist.
151        `RuntimeError`
152            If the image could not be saved.
153        `PermissionError`
154            If the path provided is not writable or does not have write permissions.
155        """
156        if isinstance(path, pathlib.Path) and not path.exists():
157            raise FileNotFoundError(f"File does not exist: {path!r}")
158
159        if self.is_missing:
160            return
161
162        mimetype = mime_type or MimeType.PNG
163        path = pathlib.Path(path)
164
165        loop = helpers.get_or_make_loop()
166        pool = concurrent.futures.ThreadPoolExecutor()
167
168        try:
169            with pool:
170                await loop.run_in_executor(
171                    pool, _write, path, file_name, mimetype, await self.read()
172                )
173                _LOGGER.info("Saved image to %s", file_name)
174
175        except asyncio.CancelledError:
176            pass
177
178        except Exception as err:
179            raise RuntimeError("Encountered an error while saving image.") from err

Saves the image to a file.

Parameters
  • file_name (str): A name for the file to save the image to.
  • path (pathlib.Path | str): A path tp save the image to.
Other Parameters
  • mime_type (MimeType | str): Optional MIME type of the image.
Raises
  • FileNotFoundError: If the path provided does not exist.
  • RuntimeError: If the image could not be saved.
  • PermissionError: If the path provided is not writable or does not have write permissions.
async def read(self) -> bytes:
181    async def read(self) -> bytes:
182        """Read this image bytes.
183
184        Returns
185        -------
186        `bytes`
187            The bytes of this image.
188        """
189        client_session = aiohttp.ClientSession()
190
191        try:
192            await client_session.__aenter__()
193            response = await client_session.get(self.create_url())
194
195            if 300 >= response.status >= 200:
196                reader = await response.read()
197
198        except Exception as exc:
199            raise RuntimeError(f"Failed to read image: {exc}") from None
200        finally:
201            await client_session.__aexit__(None, None, None)
202        return reader

Read this image bytes.

Returns
  • bytes: The bytes of this image.
async def iter(self) -> collections.abc.AsyncGenerator[bytes, None]:
204    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
205        """Iterates over the image bytes lazily.
206
207        Example
208        -------
209        import aiobungie
210
211        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
212        async for chunk in resource.iter():
213            print(chunk)
214
215        Returns
216        -------
217        `collections.AsyncGenerator[bytes, None]`
218            An async generator of the image bytes.
219        """
220
221        async for chunk in self:
222            yield chunk

Iterates over the image bytes lazily.

Example

import aiobungie

resource = aiobungie.Image("img/misc/missing_icon_d2.png") async for chunk in resource.iter(): print(chunk)

Returns
  • collections.AsyncGenerator[bytes, None]: An async generator of the image bytes.
@attrs.define(auto_exc=True)
class InternalServerError(aiobungie.HTTPException):
242@attrs.define(auto_exc=True)
243class InternalServerError(HTTPException):
244    """Raised for 5xx internal server errors."""

Raised for 5xx internal server errors.

InternalServerError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class InternalServerError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ItemBindStatus(builtins.int, aiobungie.Enum):
711@typing.final
712class ItemBindStatus(int, Enum):
713    """An enum for Destiny 2 items bind status."""
714
715    NOT_BOUND = 0
716    BOUND_TO_CHARACTER = 1
717    BOUND_TO_ACCOUNT = 2
718    BOUNT_TO_GUILD = 3

An enum for Destiny 2 items bind status.

NOT_BOUND = <ItemBindStatus.NOT_BOUND: 0>
BOUND_TO_CHARACTER = <ItemBindStatus.BOUND_TO_CHARACTER: 1>
BOUND_TO_ACCOUNT = <ItemBindStatus.BOUND_TO_ACCOUNT: 2>
BOUNT_TO_GUILD = <ItemBindStatus.BOUNT_TO_GUILD: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemLocation(builtins.int, aiobungie.Enum):
721@typing.final
722class ItemLocation(int, Enum):
723    """An enum for Destiny 2 items location."""
724
725    UNKNOWN = 0
726    INVENTORY = 1
727    VAULT = 2
728    VENDOR = 3
729    POSTMASTER = 4

An enum for Destiny 2 items location.

UNKNOWN = <ItemLocation.UNKNOWN: 0>
INVENTORY = <ItemLocation.INVENTORY: 1>
VAULT = <ItemLocation.VAULT: 2>
VENDOR = <ItemLocation.VENDOR: 3>
POSTMASTER = <ItemLocation.POSTMASTER: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemState(aiobungie.Flag):
746@typing.final
747class ItemState(Flag):
748    """An enum for Destiny 2 item states."""
749
750    NONE = 0
751    LOCKED = 1 << 0
752    TRACKED = 1 << 1
753    MASTERWORKED = 1 << 2
754    CRAFTED = 1 << 3
755    """If this bit is set, the item has been 'crafted' by the player."""
756    HIGHLITED_OBJECTIVE = 1 << 4
757    """If this bit is set, the item is a 'highlighted' objective."""

An enum for Destiny 2 item states.

NONE = <ItemState.NONE: 0>
LOCKED = <ItemState.LOCKED: 1>
TRACKED = <ItemState.TRACKED: 2>
MASTERWORKED = <ItemState.MASTERWORKED: 4>
CRAFTED = <ItemState.CRAFTED: 8>

If this bit is set, the item has been 'crafted' by the player.

HIGHLITED_OBJECTIVE = <ItemState.HIGHLITED_OBJECTIVE: 16>

If this bit is set, the item is a 'highlighted' objective.

Inherited Members
Flag
name
value
@typing.final
class ItemSubType(builtins.int, aiobungie.Enum):
578@typing.final
579class ItemSubType(int, Enum):
580    """An enum for Destiny 2 inventory items subtype."""
581
582    NONE = 0
583    AUTORIFLE = 6
584    SHOTGUN = 7
585    MACHINEGUN = 8
586    HANDCANNON = 9
587    ROCKETLAUNCHER = 10
588    FUSIONRIFLE = 11
589    SNIPERRIFLE = 12
590    PULSERIFLE = 13
591    SCOUTRIFLE = 14
592    SIDEARM = 17
593    SWORD = 18
594    MASK = 19
595    SHADER = 20
596    ORNAMENT = 21
597    FUSIONRIFLELINE = 22
598    GRENADELAUNCHER = 23
599    SUBMACHINEGUN = 24
600    TRACERIFLE = 25
601    HELMETARMOR = 26
602    GAUNTLETSARMOR = 27
603    CHESTARMOR = 28
604    LEGARMOR = 29
605    CLASSARMOR = 30
606    BOW = 31
607    DUMMYREPEATABLEBOUNTY = 32

An enum for Destiny 2 inventory items subtype.

NONE = <ItemSubType.NONE: 0>
AUTORIFLE = <ItemSubType.AUTORIFLE: 6>
SHOTGUN = <ItemSubType.SHOTGUN: 7>
MACHINEGUN = <ItemSubType.MACHINEGUN: 8>
HANDCANNON = <ItemSubType.HANDCANNON: 9>
ROCKETLAUNCHER = <ItemSubType.ROCKETLAUNCHER: 10>
FUSIONRIFLE = <ItemSubType.FUSIONRIFLE: 11>
SNIPERRIFLE = <ItemSubType.SNIPERRIFLE: 12>
PULSERIFLE = <ItemSubType.PULSERIFLE: 13>
SCOUTRIFLE = <ItemSubType.SCOUTRIFLE: 14>
SIDEARM = <ItemSubType.SIDEARM: 17>
SWORD = <ItemSubType.SWORD: 18>
MASK = <ItemSubType.MASK: 19>
SHADER = <ItemSubType.SHADER: 20>
ORNAMENT = <ItemSubType.ORNAMENT: 21>
FUSIONRIFLELINE = <ItemSubType.FUSIONRIFLELINE: 22>
GRENADELAUNCHER = <ItemSubType.GRENADELAUNCHER: 23>
SUBMACHINEGUN = <ItemSubType.SUBMACHINEGUN: 24>
TRACERIFLE = <ItemSubType.TRACERIFLE: 25>
HELMETARMOR = <ItemSubType.HELMETARMOR: 26>
GAUNTLETSARMOR = <ItemSubType.GAUNTLETSARMOR: 27>
CHESTARMOR = <ItemSubType.CHESTARMOR: 28>
LEGARMOR = <ItemSubType.LEGARMOR: 29>
CLASSARMOR = <ItemSubType.CLASSARMOR: 30>
BOW = <ItemSubType.BOW: 31>
DUMMYREPEATABLEBOUNTY = <ItemSubType.DUMMYREPEATABLEBOUNTY: 32>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemTier(builtins.int, aiobungie.Enum):
610@typing.final
611class ItemTier(int, Enum):
612    """An enum for a Destiny 2 item tier."""
613
614    NONE = 0
615    BASIC = 3340296461
616    COMMON = 2395677314
617    RARE = 2127292149
618    LEGENDERY = 4008398120
619    EXOTIC = 2759499571

An enum for a Destiny 2 item tier.

NONE = <ItemTier.NONE: 0>
BASIC = <ItemTier.BASIC: 3340296461>
COMMON = <ItemTier.COMMON: 2395677314>
RARE = <ItemTier.RARE: 2127292149>
LEGENDERY = <ItemTier.LEGENDERY: 4008398120>
EXOTIC = <ItemTier.EXOTIC: 2759499571>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemType(builtins.int, aiobungie.Enum):
545@typing.final
546class ItemType(int, Enum):
547    """Enums for Destiny2's item types."""
548
549    NONE = 0
550    CURRENCY = 1
551    ARMOR = 2
552    WEAPON = 3
553    MESSAGE = 7
554    ENGRAM = 8
555    CONSUMABLE = 9
556    EXCHANGEMATERIAL = 10
557    MISSIONREWARD = 11
558    QUESTSTEP = 12
559    QUESTSTEPCOMPLETE = 13
560    EMBLEM = 14
561    QUEST = 15
562    SUBCLASS = 16
563    CLANBANNER = 17
564    AURA = 18
565    MOD = 19
566    DUMMY = 20
567    SHIP = 21
568    VEHICLE = 22
569    EMOTE = 23
570    GHOST = 24
571    PACKAGE = 25
572    BOUNTY = 26
573    WRAPPER = 27
574    SEASONALARTIFACT = 28
575    FINISHER = 29

Enums for Destiny2's item types.

NONE = <ItemType.NONE: 0>
CURRENCY = <ItemType.CURRENCY: 1>
ARMOR = <ItemType.ARMOR: 2>
WEAPON = <ItemType.WEAPON: 3>
MESSAGE = <ItemType.MESSAGE: 7>
ENGRAM = <ItemType.ENGRAM: 8>
CONSUMABLE = <ItemType.CONSUMABLE: 9>
EXCHANGEMATERIAL = <ItemType.EXCHANGEMATERIAL: 10>
MISSIONREWARD = <ItemType.MISSIONREWARD: 11>
QUESTSTEP = <ItemType.QUESTSTEP: 12>
QUESTSTEPCOMPLETE = <ItemType.QUESTSTEPCOMPLETE: 13>
EMBLEM = <ItemType.EMBLEM: 14>
QUEST = <ItemType.QUEST: 15>
SUBCLASS = <ItemType.SUBCLASS: 16>
CLANBANNER = <ItemType.CLANBANNER: 17>
AURA = <ItemType.AURA: 18>
MOD = <ItemType.MOD: 19>
DUMMY = <ItemType.DUMMY: 20>
SHIP = <ItemType.SHIP: 21>
VEHICLE = <ItemType.VEHICLE: 22>
EMOTE = <ItemType.EMOTE: 23>
GHOST = <ItemType.GHOST: 24>
PACKAGE = <ItemType.PACKAGE: 25>
BOUNTY = <ItemType.BOUNTY: 26>
WRAPPER = <ItemType.WRAPPER: 27>
SEASONALARTIFACT = <ItemType.SEASONALARTIFACT: 28>
FINISHER = <ItemType.FINISHER: 29>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Iterator(typing.Generic[~Item], collections.abc.Iterator[~Item]):
 47class Iterator(typing.Generic[Item], collections.Iterator[Item]):
 48    """A Flat, In-Memory iterator for sequenced based data.
 49
 50    Example
 51    -------
 52    ```py
 53    iterator = Iterator([1, 2, 3])
 54
 55    # Map the results.
 56    for item in iterator.map(lambda item: item * 2):
 57        print(item)
 58    # 2
 59    # 4
 60
 61    # Indexing is also supported.
 62    print(iterator[0])
 63    # 1
 64
 65    # Normal iteration.
 66    for item in iterator:
 67        print(item)
 68    # 1
 69    # 2
 70    # 3
 71
 72    # Union two iterators.
 73    iterator2 = Iterator([4, 5, 6])
 74    final = iterator | iterator2
 75    # <Iterator([1, 2, 3, 4, 5, 6])>
 76    ```
 77
 78    Parameters
 79    ----------
 80    items: `collections.Iterable[Item]`
 81        The items to iterate over.
 82    """
 83
 84    __slots__ = ("_items",)
 85
 86    def __init__(self, items: collections.Iterable[Item]) -> None:
 87        self._items = _builtins.iter(items)
 88
 89    @typing.overload
 90    def collect(self) -> list[Item]:
 91        ...
 92
 93    @typing.overload
 94    def collect(self, casting: _B) -> list[_B]:
 95        ...
 96
 97    def copied(self) -> Iterator[Item]:
 98        """Creates an iterator which `deeply` copies all of its elements.
 99
100        Example
101        -------
102        ```py
103        it = Iterator([None, None, None])
104        copied_iter = it.copied()
105        assert it.collect() == copied.collect()
106        ```
107        """
108        return self.__class__(_copy.deepcopy(self._items))
109
110    def collect(
111        self, casting: typing.Optional[_B] = None
112    ) -> typing.Union[list[Item], list[_B]]:
113        """Collects all items in the iterator into a list and cast them into an object if provided.
114
115        Example
116        -------
117        >>> iterator = Iterator([1, 2, 3])
118        >>> iterator.collect(casting=str)
119        ["1", "2", "3"]
120
121        Parameters
122        ----------
123        casting: `T | None`
124            The type to cast the items to. If `None` is provided, the items will be returned as is.
125
126        Raises
127        ------
128        `StopIteration`
129            If no elements are left in the iterator.
130        """
131        if casting is not None:
132            return typing.cast(list[_B], list(map(casting, self._items)))
133
134        return list(self._items)
135
136    def next(self) -> Item:
137        """Returns the next item in the iterator.
138
139        Example
140        -------
141        ```py
142        iterator = Iterator(["1", "2", "3"])
143        item = iterator.next()
144        assert item == "1"
145        item = iterator.next()
146        assert item == "2"
147        ```
148
149        Raises
150        ------
151        `StopIteration`
152            If no elements are left in the iterator.
153        """
154        try:
155            return self.__next__()
156        except StopIteration:
157            self._ok()
158
159    def map(
160        self, predicate: collections.Callable[[Item], OtherItem]
161    ) -> Iterator[OtherItem]:
162        """Maps each item in the iterator to its predicated value.
163
164        Example
165        -------
166        ```py
167        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
168        print(iterator)
169        # <Iterator([1, 2, 3])>
170        ```
171
172        Parameters
173        ----------
174        predicate: `collections.Callable[[Item], OtherItem]`
175            The function to map each item in the iterator to its predicated value.
176
177        Returns
178        -------
179        `Iterator[OtherItem]`
180            The mapped iterator.
181
182        Raises
183        ------
184        `StopIteration`
185            If no elements are left in the iterator.
186        """
187        return Iterator(map(predicate, self._items))
188
189    def take(self, n: int) -> Iterator[Item]:
190        """Take the first number of items until the number of items are yielded or
191        the end of the iterator is reached.
192
193        Example
194        -------
195        ```py
196        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
197        print(iterator.take(2))
198        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
199        ```
200
201        Parameters
202        ----------
203        n: `int`
204            The number of items to take.
205
206        Raises
207        ------
208        `StopIteration`
209            If no elements are left in the iterator.
210        """
211        return Iterator(itertools.islice(self._items, n))
212
213    def take_while(
214        self, predicate: collections.Callable[[Item], bool]
215    ) -> Iterator[Item]:
216        """Yields items from the iterator while predicate returns `True`.
217
218        Example
219        -------
220        ```py
221        iterator = Iterator([STEAM, XBOX, STADIA])
222        print(iterator.take_while(lambda platform: platform is not XBOX))
223        # <Iterator([STEAM])>
224        ```
225
226        Parameters
227        ----------
228        predicate: `collections.Callable[[Item], bool]`
229            The function to predicate each item in the iterator.
230
231        Raises
232        ------
233        `StopIteration`
234            If no elements are left in the iterator.
235        """
236        return Iterator(itertools.takewhile(predicate, self._items))
237
238    def drop_while(
239        self, predicate: collections.Callable[[Item], bool]
240    ) -> Iterator[Item]:
241        """Yields items from the iterator while predicate returns `False`.
242
243        Example
244        -------
245        ```py
246        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
247        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
248        # <Iterator([DestinyMembership(name="Bob")])>
249        ```
250
251        Parameters
252        ----------
253        predicate: `collections.Callable[[Item], bool]`
254            The function to predicate each item in the iterator.
255
256        Raises
257        ------
258        `StopIteration`
259            If no elements are left in the iterator.
260        """
261        return Iterator(itertools.dropwhile(predicate, self._items))
262
263    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
264        """Filters the iterator to only yield items that match the predicate.
265
266        Example
267        -------
268        ```py
269        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
270        print(names.filter(lambda n: n != "Jim"))
271        # <Iterator(["Bob", "Mike", "Jess"])>
272        ```
273        """
274        return Iterator(filter(predicate, self._items))
275
276    def skip(self, n: int) -> Iterator[Item]:
277        """Skips the first number of items in the iterator.
278
279        Example
280        -------
281        ```py
282        iterator = Iterator([STEAM, XBOX, STADIA])
283        print(iterator.skip(1))
284        # <Iterator([XBOX, STADIA])>
285        ```
286        """
287        return Iterator(itertools.islice(self._items, n, None))
288
289    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
290        """Zips the iterator with another iterable.
291
292        Example
293        -------
294        ```py
295        iterator = Iterator([1, 3, 5])
296        other = Iterator([2, 4, 6])
297        for item, other_item in iterator.zip(other):
298            print(item, other_item)
299        # <Iterator([(1, 2), (3, 4), (5, 6)])>
300        ```
301
302        Parameters
303        ----------
304        other: `Iterator[OtherItem]`
305            The iterable to zip with.
306
307        Raises
308        ------
309        `StopIteration`
310            If no elements are left in the iterator.
311        """
312        return Iterator(zip(self._items, other))
313
314    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
315        """`True` if all items in the iterator match the predicate.
316
317        Example
318        -------
319        ```py
320        iterator = Iterator([1, 2, 3])
321        while iterator.all(lambda item: isinstance(item, int)):
322            print("Still all integers")
323            continue
324        # Still all integers
325        ```
326
327        Parameters
328        ----------
329        predicate: `collections.Callable[[Item], bool]`
330            The function to test each item in the iterator.
331
332        Raises
333        ------
334        `StopIteration`
335            If no elements are left in the iterator.
336        """
337        return all(predicate(item) for item in self)
338
339    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
340        """`True` if any items in the iterator match the predicate.
341
342        Example
343        -------
344        ```py
345        iterator = Iterator([1, 2, 3])
346        if iterator.any(lambda item: isinstance(item, int)):
347            print("At least one item is an int.")
348        # At least one item is an int.
349        ```
350
351        Parameters
352        ----------
353        predicate: `collections.Callable[[Item], bool]`
354            The function to test each item in the iterator.
355
356        Raises
357        ------
358        `StopIteration`
359            If no elements are left in the iterator.
360        """
361        return any(predicate(item) for item in self)
362
363    def sort(
364        self,
365        *,
366        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
367        reverse: bool = False,
368    ) -> Iterator[Item]:
369        """Sorts the iterator.
370
371        Example
372        -------
373        ```py
374        iterator = Iterator([3, 1, 6, 7])
375        print(iterator.sort(key=lambda item: item))
376        # <Iterator([1, 3, 6, 7])>
377        ```
378
379        Parameters
380        ----------
381        key: `collections.Callable[[Item], Any]`
382            The function to sort by.
383        reverse: `bool`
384            Whether to reverse the sort.
385
386        Raises
387        ------
388        `StopIteration`
389            If no elements are left in the iterator.
390        """
391        return Iterator(sorted(self._items, key=key, reverse=reverse))
392
393    def first(self) -> Item:
394        """Returns the first item in the iterator.
395
396        Example
397        -------
398        ```py
399        iterator = Iterator([3, 1, 6, 7])
400        print(iterator.first())
401        3
402        ```
403
404        Raises
405        ------
406        `StopIteration`
407            If no elements are left in the iterator.
408        """
409        return self.take(1).next()
410
411    def reversed(self) -> Iterator[Item]:
412        """Returns a new iterator that yields the items in the iterator in reverse order.
413
414        Example
415        -------
416        ```py
417        iterator = Iterator([3, 1, 6, 7])
418        print(iterator.reversed())
419        # <Iterator([7, 6, 1, 3])>
420        ```
421
422        Raises
423        ------
424        `StopIteration`
425            If no elements are left in the iterator.
426        """
427        return Iterator(reversed(self.collect()))
428
429    def count(self) -> int:
430        """Returns the number of items in the iterator.
431
432        Example
433        -------
434        ```py
435        iterator = Iterator([3, 1, 6, 7])
436        print(iterator.count())
437        4
438        ```
439        """
440        count = 0
441        for _ in self:
442            count += 1
443
444        return count
445
446    def union(self, other: Iterator[Item]) -> Iterator[Item]:
447        """Returns a new iterator that yields all items from both iterators.
448
449        Example
450        -------
451        ```py
452        iterator = Iterator([1, 2, 3])
453        other = Iterator([4, 5, 6])
454        print(iterator.union(other))
455        # <Iterator([1, 2, 3, 4, 5, 6])>
456        ```
457
458        Parameters
459        ----------
460        other: `Iterator[Item]`
461            The iterable to union with.
462
463        Raises
464        ------
465        `StopIteration`
466            If no elements are left in the iterator.
467        """
468        return Iterator(itertools.chain(self._items, other))
469
470    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
471        """Calls the function on each item in the iterator.
472
473        Example
474        -------
475        ```py
476        iterator = Iterator([1, 2, 3])
477        iterator.for_each(lambda item: print(item))
478        # 1
479        # 2
480        # 3
481        ```
482
483        Parameters
484        ----------
485        func: `typeshed.Callable[[Item], None]`
486            The function to call on each item in the iterator.
487        """
488        for item in self:
489            func(item)
490
491    async def async_for_each(
492        self,
493        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
494    ) -> None:
495        """Calls the async function on each item in the iterator concurrently.
496
497        Example
498        -------
499        ```py
500        async def signup(username: str) -> None:
501            async with aiohttp.request('POST', '...') as r:
502                # Actual logic.
503                ...
504
505        async def main():
506            users = aiobungie.into_iter(["user_danny", "user_jojo"])
507            await users.async_for_each(lambda username: signup(username))
508        ```
509
510        Parameters
511        ----------
512        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
513            The async function to call on each item in the iterator.
514        """
515        await _helpers.awaits(*(func(item) for item in self))
516
517    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
518        """Returns a new iterator that yields tuples of the index and item.
519
520        Example
521        -------
522        ```py
523        iterator = Iterator([1, 2, 3])
524        for index, item in iterator.enumerate():
525            print(index, item)
526        # 0 1
527        # 1 2
528        # 2 3
529        ```
530
531        Raises
532        ------
533        `StopIteration`
534            If no elements are left in the iterator.
535        """
536        return Iterator(enumerate(self._items, start=start))
537
538    def _ok(self) -> typing.NoReturn:
539        raise StopIteration("No more items in the iterator.") from None
540
541    def __getitem__(self, index: int) -> Item:
542        try:
543            return self.skip(index).first()
544        except IndexError:
545            self._ok()
546
547    def __or__(self, other: Iterator[Item]) -> Iterator[Item]:
548        return self.union(other)
549
550    # This is a never.
551    def __setitem__(self) -> typing.NoReturn:
552        raise TypeError(
553            f"{type(self).__name__} doesn't support item assignment."
554        ) from None
555
556    def __repr__(self) -> str:
557        return f'<{self.__class__.__name__}({", ".join([str(item) for item in self])})>'
558
559    def __len__(self) -> int:
560        return self.count()
561
562    def __iter__(self) -> Iterator[Item]:
563        return self
564
565    def __next__(self) -> Item:
566        try:
567            item = next(self._items)
568        except StopIteration:
569            self._ok()
570
571        return item

A Flat, In-Memory iterator for sequenced based data.

Example
iterator = Iterator([1, 2, 3])

# Map the results.
for item in iterator.map(lambda item: item * 2):
    print(item)
# 2
# 4

# Indexing is also supported.
print(iterator[0])
# 1

# Normal iteration.
for item in iterator:
    print(item)
# 1
# 2
# 3

# Union two iterators.
iterator2 = Iterator([4, 5, 6])
final = iterator | iterator2
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • items (collections.Iterable[Item]): The items to iterate over.
Iterator(items: collections.abc.Iterable[~Item])
86    def __init__(self, items: collections.Iterable[Item]) -> None:
87        self._items = _builtins.iter(items)
def collect( self, casting: 'typing.Optional[_B]' = None) -> 'typing.Union[list[Item], list[_B]]':
110    def collect(
111        self, casting: typing.Optional[_B] = None
112    ) -> typing.Union[list[Item], list[_B]]:
113        """Collects all items in the iterator into a list and cast them into an object if provided.
114
115        Example
116        -------
117        >>> iterator = Iterator([1, 2, 3])
118        >>> iterator.collect(casting=str)
119        ["1", "2", "3"]
120
121        Parameters
122        ----------
123        casting: `T | None`
124            The type to cast the items to. If `None` is provided, the items will be returned as is.
125
126        Raises
127        ------
128        `StopIteration`
129            If no elements are left in the iterator.
130        """
131        if casting is not None:
132            return typing.cast(list[_B], list(map(casting, self._items)))
133
134        return list(self._items)

Collects all items in the iterator into a list and cast them into an object if provided.

Example
>>> iterator = Iterator([1, 2, 3])
>>> iterator.collect(casting=str)
["1", "2", "3"]
Parameters
  • casting (T | None): The type to cast the items to. If None is provided, the items will be returned as is.
Raises
  • StopIteration: If no elements are left in the iterator.
def copied(self) -> Iterator[~Item]:
 97    def copied(self) -> Iterator[Item]:
 98        """Creates an iterator which `deeply` copies all of its elements.
 99
100        Example
101        -------
102        ```py
103        it = Iterator([None, None, None])
104        copied_iter = it.copied()
105        assert it.collect() == copied.collect()
106        ```
107        """
108        return self.__class__(_copy.deepcopy(self._items))

Creates an iterator which deeply copies all of its elements.

Example
it = Iterator([None, None, None])
copied_iter = it.copied()
assert it.collect() == copied.collect()
def next(self) -> ~Item:
136    def next(self) -> Item:
137        """Returns the next item in the iterator.
138
139        Example
140        -------
141        ```py
142        iterator = Iterator(["1", "2", "3"])
143        item = iterator.next()
144        assert item == "1"
145        item = iterator.next()
146        assert item == "2"
147        ```
148
149        Raises
150        ------
151        `StopIteration`
152            If no elements are left in the iterator.
153        """
154        try:
155            return self.__next__()
156        except StopIteration:
157            self._ok()

Returns the next item in the iterator.

Example
iterator = Iterator(["1", "2", "3"])
item = iterator.next()
assert item == "1"
item = iterator.next()
assert item == "2"
Raises
  • StopIteration: If no elements are left in the iterator.
def map( self, predicate: 'collections.Callable[[Item], OtherItem]') -> 'Iterator[OtherItem]':
159    def map(
160        self, predicate: collections.Callable[[Item], OtherItem]
161    ) -> Iterator[OtherItem]:
162        """Maps each item in the iterator to its predicated value.
163
164        Example
165        -------
166        ```py
167        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
168        print(iterator)
169        # <Iterator([1, 2, 3])>
170        ```
171
172        Parameters
173        ----------
174        predicate: `collections.Callable[[Item], OtherItem]`
175            The function to map each item in the iterator to its predicated value.
176
177        Returns
178        -------
179        `Iterator[OtherItem]`
180            The mapped iterator.
181
182        Raises
183        ------
184        `StopIteration`
185            If no elements are left in the iterator.
186        """
187        return Iterator(map(predicate, self._items))

Maps each item in the iterator to its predicated value.

Example
iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
print(iterator)
# <Iterator([1, 2, 3])>
Parameters
  • predicate (collections.Callable[[Item], OtherItem]): The function to map each item in the iterator to its predicated value.
Returns
  • Iterator[OtherItem]: The mapped iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def take(self, n: int) -> Iterator[~Item]:
189    def take(self, n: int) -> Iterator[Item]:
190        """Take the first number of items until the number of items are yielded or
191        the end of the iterator is reached.
192
193        Example
194        -------
195        ```py
196        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
197        print(iterator.take(2))
198        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
199        ```
200
201        Parameters
202        ----------
203        n: `int`
204            The number of items to take.
205
206        Raises
207        ------
208        `StopIteration`
209            If no elements are left in the iterator.
210        """
211        return Iterator(itertools.islice(self._items, n))

Take the first number of items until the number of items are yielded or the end of the iterator is reached.

Example
iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
print(iterator.take(2))
# <Iterator([GameMode.RAID, GameMode.STRIKE])>
Parameters
  • n (int): The number of items to take.
Raises
  • StopIteration: If no elements are left in the iterator.
def take_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> Iterator[~Item]:
213    def take_while(
214        self, predicate: collections.Callable[[Item], bool]
215    ) -> Iterator[Item]:
216        """Yields items from the iterator while predicate returns `True`.
217
218        Example
219        -------
220        ```py
221        iterator = Iterator([STEAM, XBOX, STADIA])
222        print(iterator.take_while(lambda platform: platform is not XBOX))
223        # <Iterator([STEAM])>
224        ```
225
226        Parameters
227        ----------
228        predicate: `collections.Callable[[Item], bool]`
229            The function to predicate each item in the iterator.
230
231        Raises
232        ------
233        `StopIteration`
234            If no elements are left in the iterator.
235        """
236        return Iterator(itertools.takewhile(predicate, self._items))

Yields items from the iterator while predicate returns True.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.take_while(lambda platform: platform is not XBOX))
# <Iterator([STEAM])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def drop_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> Iterator[~Item]:
238    def drop_while(
239        self, predicate: collections.Callable[[Item], bool]
240    ) -> Iterator[Item]:
241        """Yields items from the iterator while predicate returns `False`.
242
243        Example
244        -------
245        ```py
246        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
247        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
248        # <Iterator([DestinyMembership(name="Bob")])>
249        ```
250
251        Parameters
252        ----------
253        predicate: `collections.Callable[[Item], bool]`
254            The function to predicate each item in the iterator.
255
256        Raises
257        ------
258        `StopIteration`
259            If no elements are left in the iterator.
260        """
261        return Iterator(itertools.dropwhile(predicate, self._items))

Yields items from the iterator while predicate returns False.

Example
iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
# <Iterator([DestinyMembership(name="Bob")])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def filter( self, predicate: collections.abc.Callable[[~Item], bool]) -> Iterator[~Item]:
263    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
264        """Filters the iterator to only yield items that match the predicate.
265
266        Example
267        -------
268        ```py
269        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
270        print(names.filter(lambda n: n != "Jim"))
271        # <Iterator(["Bob", "Mike", "Jess"])>
272        ```
273        """
274        return Iterator(filter(predicate, self._items))

Filters the iterator to only yield items that match the predicate.

Example
names = Iterator(["Jim", "Bob", "Mike", "Jess"])
print(names.filter(lambda n: n != "Jim"))
# <Iterator(["Bob", "Mike", "Jess"])>
def skip(self, n: int) -> Iterator[~Item]:
276    def skip(self, n: int) -> Iterator[Item]:
277        """Skips the first number of items in the iterator.
278
279        Example
280        -------
281        ```py
282        iterator = Iterator([STEAM, XBOX, STADIA])
283        print(iterator.skip(1))
284        # <Iterator([XBOX, STADIA])>
285        ```
286        """
287        return Iterator(itertools.islice(self._items, n, None))

Skips the first number of items in the iterator.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.skip(1))
# <Iterator([XBOX, STADIA])>
def zip(self, other: 'Iterator[OtherItem]') -> 'Iterator[tuple[Item, OtherItem]]':
289    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
290        """Zips the iterator with another iterable.
291
292        Example
293        -------
294        ```py
295        iterator = Iterator([1, 3, 5])
296        other = Iterator([2, 4, 6])
297        for item, other_item in iterator.zip(other):
298            print(item, other_item)
299        # <Iterator([(1, 2), (3, 4), (5, 6)])>
300        ```
301
302        Parameters
303        ----------
304        other: `Iterator[OtherItem]`
305            The iterable to zip with.
306
307        Raises
308        ------
309        `StopIteration`
310            If no elements are left in the iterator.
311        """
312        return Iterator(zip(self._items, other))

Zips the iterator with another iterable.

Example
iterator = Iterator([1, 3, 5])
other = Iterator([2, 4, 6])
for item, other_item in iterator.zip(other):
    print(item, other_item)
# <Iterator([(1, 2), (3, 4), (5, 6)])>
Parameters
  • other (Iterator[OtherItem]): The iterable to zip with.
Raises
  • StopIteration: If no elements are left in the iterator.
def all(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
314    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
315        """`True` if all items in the iterator match the predicate.
316
317        Example
318        -------
319        ```py
320        iterator = Iterator([1, 2, 3])
321        while iterator.all(lambda item: isinstance(item, int)):
322            print("Still all integers")
323            continue
324        # Still all integers
325        ```
326
327        Parameters
328        ----------
329        predicate: `collections.Callable[[Item], bool]`
330            The function to test each item in the iterator.
331
332        Raises
333        ------
334        `StopIteration`
335            If no elements are left in the iterator.
336        """
337        return all(predicate(item) for item in self)

True if all items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
    print("Still all integers")
    continue
# Still all integers
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def any(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
339    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
340        """`True` if any items in the iterator match the predicate.
341
342        Example
343        -------
344        ```py
345        iterator = Iterator([1, 2, 3])
346        if iterator.any(lambda item: isinstance(item, int)):
347            print("At least one item is an int.")
348        # At least one item is an int.
349        ```
350
351        Parameters
352        ----------
353        predicate: `collections.Callable[[Item], bool]`
354            The function to test each item in the iterator.
355
356        Raises
357        ------
358        `StopIteration`
359            If no elements are left in the iterator.
360        """
361        return any(predicate(item) for item in self)

True if any items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
    print("At least one item is an int.")
# At least one item is an int.
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def sort( self, *, key: 'collections.Callable[[Item], typeshed.SupportsRichComparison]', reverse: bool = False) -> Iterator[~Item]:
363    def sort(
364        self,
365        *,
366        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
367        reverse: bool = False,
368    ) -> Iterator[Item]:
369        """Sorts the iterator.
370
371        Example
372        -------
373        ```py
374        iterator = Iterator([3, 1, 6, 7])
375        print(iterator.sort(key=lambda item: item))
376        # <Iterator([1, 3, 6, 7])>
377        ```
378
379        Parameters
380        ----------
381        key: `collections.Callable[[Item], Any]`
382            The function to sort by.
383        reverse: `bool`
384            Whether to reverse the sort.
385
386        Raises
387        ------
388        `StopIteration`
389            If no elements are left in the iterator.
390        """
391        return Iterator(sorted(self._items, key=key, reverse=reverse))

Sorts the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.sort(key=lambda item: item))
# <Iterator([1, 3, 6, 7])>
Parameters
  • key (collections.Callable[[Item], Any]): The function to sort by.
  • reverse (bool): Whether to reverse the sort.
Raises
  • StopIteration: If no elements are left in the iterator.
def first(self) -> ~Item:
393    def first(self) -> Item:
394        """Returns the first item in the iterator.
395
396        Example
397        -------
398        ```py
399        iterator = Iterator([3, 1, 6, 7])
400        print(iterator.first())
401        3
402        ```
403
404        Raises
405        ------
406        `StopIteration`
407            If no elements are left in the iterator.
408        """
409        return self.take(1).next()

Returns the first item in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.first())
3
Raises
  • StopIteration: If no elements are left in the iterator.
def reversed(self) -> Iterator[~Item]:
411    def reversed(self) -> Iterator[Item]:
412        """Returns a new iterator that yields the items in the iterator in reverse order.
413
414        Example
415        -------
416        ```py
417        iterator = Iterator([3, 1, 6, 7])
418        print(iterator.reversed())
419        # <Iterator([7, 6, 1, 3])>
420        ```
421
422        Raises
423        ------
424        `StopIteration`
425            If no elements are left in the iterator.
426        """
427        return Iterator(reversed(self.collect()))

Returns a new iterator that yields the items in the iterator in reverse order.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.reversed())
# <Iterator([7, 6, 1, 3])>
Raises
  • StopIteration: If no elements are left in the iterator.
def count(self) -> int:
429    def count(self) -> int:
430        """Returns the number of items in the iterator.
431
432        Example
433        -------
434        ```py
435        iterator = Iterator([3, 1, 6, 7])
436        print(iterator.count())
437        4
438        ```
439        """
440        count = 0
441        for _ in self:
442            count += 1
443
444        return count

Returns the number of items in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.count())
4
def union( self, other: Iterator[~Item]) -> Iterator[~Item]:
446    def union(self, other: Iterator[Item]) -> Iterator[Item]:
447        """Returns a new iterator that yields all items from both iterators.
448
449        Example
450        -------
451        ```py
452        iterator = Iterator([1, 2, 3])
453        other = Iterator([4, 5, 6])
454        print(iterator.union(other))
455        # <Iterator([1, 2, 3, 4, 5, 6])>
456        ```
457
458        Parameters
459        ----------
460        other: `Iterator[Item]`
461            The iterable to union with.
462
463        Raises
464        ------
465        `StopIteration`
466            If no elements are left in the iterator.
467        """
468        return Iterator(itertools.chain(self._items, other))

Returns a new iterator that yields all items from both iterators.

Example
iterator = Iterator([1, 2, 3])
other = Iterator([4, 5, 6])
print(iterator.union(other))
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • other (Iterator[Item]): The iterable to union with.
Raises
  • StopIteration: If no elements are left in the iterator.
def for_each(self, func: collections.abc.Callable[[~Item], typing.Any]) -> None:
470    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
471        """Calls the function on each item in the iterator.
472
473        Example
474        -------
475        ```py
476        iterator = Iterator([1, 2, 3])
477        iterator.for_each(lambda item: print(item))
478        # 1
479        # 2
480        # 3
481        ```
482
483        Parameters
484        ----------
485        func: `typeshed.Callable[[Item], None]`
486            The function to call on each item in the iterator.
487        """
488        for item in self:
489            func(item)

Calls the function on each item in the iterator.

Example
iterator = Iterator([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
  • func (typeshed.Callable[[Item], None]): The function to call on each item in the iterator.
async def async_for_each( self, func: collections.abc.Callable[[~Item], collections.abc.Coroutine[None, None, None]]) -> None:
491    async def async_for_each(
492        self,
493        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
494    ) -> None:
495        """Calls the async function on each item in the iterator concurrently.
496
497        Example
498        -------
499        ```py
500        async def signup(username: str) -> None:
501            async with aiohttp.request('POST', '...') as r:
502                # Actual logic.
503                ...
504
505        async def main():
506            users = aiobungie.into_iter(["user_danny", "user_jojo"])
507            await users.async_for_each(lambda username: signup(username))
508        ```
509
510        Parameters
511        ----------
512        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
513            The async function to call on each item in the iterator.
514        """
515        await _helpers.awaits(*(func(item) for item in self))

Calls the async function on each item in the iterator concurrently.

Example
async def signup(username: str) -> None:
    async with aiohttp.request('POST', '...') as r:
        # Actual logic.
        ...

async def main():
    users = aiobungie.into_iter(["user_danny", "user_jojo"])
    await users.async_for_each(lambda username: signup(username))
Parameters
  • func (collections.Callable[[Item], collections.Coroutine[None, None, None]]): The async function to call on each item in the iterator.
def enumerate( self, *, start: int = 0) -> Iterator[tuple[int, ~Item]]:
517    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
518        """Returns a new iterator that yields tuples of the index and item.
519
520        Example
521        -------
522        ```py
523        iterator = Iterator([1, 2, 3])
524        for index, item in iterator.enumerate():
525            print(index, item)
526        # 0 1
527        # 1 2
528        # 2 3
529        ```
530
531        Raises
532        ------
533        `StopIteration`
534            If no elements are left in the iterator.
535        """
536        return Iterator(enumerate(self._items, start=start))

Returns a new iterator that yields tuples of the index and item.

Example
iterator = Iterator([1, 2, 3])
for index, item in iterator.enumerate():
    print(index, item)
# 0 1
# 1 2
# 2 3
Raises
  • StopIteration: If no elements are left in the iterator.
@typing.final
class MembershipOption(builtins.int, aiobungie.Enum):
702@typing.final
703class MembershipOption(int, Enum):
704    """A enum for GroupV2 membership options."""
705
706    REVIEWD = 0
707    OPEN = 1
708    CLOSED = 2

A enum for GroupV2 membership options.

REVIEWD = <MembershipOption.REVIEWD: 0>
OPEN = <MembershipOption.OPEN: 1>
CLOSED = <MembershipOption.CLOSED: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class MembershipType(builtins.int, aiobungie.Enum):
450@typing.final
451class MembershipType(int, Enum):
452    """An Enum for Bungie membership types."""
453
454    NONE = 0
455    XBOX = 1
456    PSN = 2
457    STEAM = 3
458    BLIZZARD = 4
459    STADIA = 5
460    EPIC_GAMES_STORE = 6
461    DEMON = 10
462    BUNGIE = 254
463    ALL = -1

An Enum for Bungie membership types.

NONE = <MembershipType.NONE: 0>
XBOX = <MembershipType.XBOX: 1>
PSN = <MembershipType.PSN: 2>
STEAM = <MembershipType.STEAM: 3>
BLIZZARD = <MembershipType.BLIZZARD: 4>
STADIA = <MembershipType.STADIA: 5>
EPIC_GAMES_STORE = <MembershipType.EPIC_GAMES_STORE: 6>
DEMON = <MembershipType.DEMON: 10>
BUNGIE = <MembershipType.BUNGIE: 254>
ALL = <MembershipType.ALL: -1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class MembershipTypeError(aiobungie.BadRequest):
181@attrs.define(auto_exc=True)
182class MembershipTypeError(BadRequest):
183    """A bad request error raised when passing wrong membership to the request.
184
185    Those fields are useful since it returns the correct membership and id which can be used
186    to make the request again with those fields.
187
188    Example
189    -------
190    ```py
191    try:
192        profile = await client.fetch_profile(
193            member_id=1,
194            type=aiobungie.MembershipType.STADIA,
195            components=[]
196        )
197
198    # Membership type is wrong!
199    except aiobungie.MembershipTypeError as err:
200        correct_membersip = err.into_membership()
201        profile_id = err.membership_id
202
203        # Recall the method.
204        profile = await client.fetch_profile(
205            member_id=profile_id,
206            type=correct_membership,
207            components=[]
208        )
209    ```
210    """
211
212    membership_type: str
213    """The errored membership type passed to the request."""
214
215    membership_id: int
216    """The errored user's membership id."""
217
218    required_membership: str
219    """The required correct membership for errored user."""
220
221    def into_membership(
222        self, value: typing.Optional[str] = None
223    ) -> enums.MembershipType:
224        """Turn the required membership from `str` into `aiobungie.Membership` type.
225
226        If value parameter is not provided it will fall back to the required membership.
227        """
228        if value is None:
229            return _DETERMINE_MSHIP(self.required_membership)
230        return _DETERMINE_MSHIP(value)
231
232    def __str__(self) -> str:
233        return (
234            f"Expected membership: {self.into_membership().name.replace('_', '').title()}, "
235            f"But got {self.into_membership(self.membership_type)} for id {self.membership_id}"
236        )
237
238    def __int__(self) -> int:
239        return int(self.membership_id)

A bad request error raised when passing wrong membership to the request.

Those fields are useful since it returns the correct membership and id which can be used to make the request again with those fields.

Example
try:
    profile = await client.fetch_profile(
        member_id=1,
        type=aiobungie.MembershipType.STADIA,
        components=[]
    )

# Membership type is wrong!
except aiobungie.MembershipTypeError as err:
    correct_membersip = err.into_membership()
    profile_id = err.membership_id

    # Recall the method.
    profile = await client.fetch_profile(
        member_id=profile_id,
        type=correct_membership,
        components=[]
    )
MembershipTypeError( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], membership_type: str, membership_id: int, required_membership: str)
 2def __init__(self, message, url, body, headers, membership_type, membership_id, required_membership):
 3    self.message = message
 4    self.url = url
 5    self.body = body
 6    self.headers = headers
 7    self.http_status = attr_dict['http_status'].default
 8    self.membership_type = membership_type
 9    self.membership_id = membership_id
10    self.required_membership = required_membership
11    BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.membership_type,self.membership_id,self.required_membership)

Method generated by attrs for class MembershipTypeError.

membership_type: str

The errored membership type passed to the request.

membership_id: int

The errored user's membership id.

required_membership: str

The required correct membership for errored user.

def into_membership( self, value: Optional[str] = None) -> MembershipType:
221    def into_membership(
222        self, value: typing.Optional[str] = None
223    ) -> enums.MembershipType:
224        """Turn the required membership from `str` into `aiobungie.Membership` type.
225
226        If value parameter is not provided it will fall back to the required membership.
227        """
228        if value is None:
229            return _DETERMINE_MSHIP(self.required_membership)
230        return _DETERMINE_MSHIP(value)

Turn the required membership from str into aiobungie.Membership type.

If value parameter is not provided it will fall back to the required membership.

Inherited Members
BadRequest
url
body
headers
http_status
HTTPError
message
builtins.BaseException
with_traceback
args
@typing.final
class MilestoneType(builtins.int, aiobungie.Enum):
495@typing.final
496class MilestoneType(int, Enum):
497    """An Enum for Destiny 2 milestone types."""
498
499    UNKNOWN = 0
500    TUTORIAL = 1
501    ONETIME = 2
502    WEEKLY = 3
503    DAILY = 4
504    SPECIAL = 5

An Enum for Destiny 2 milestone types.

UNKNOWN = <MilestoneType.UNKNOWN: 0>
TUTORIAL = <MilestoneType.TUTORIAL: 1>
ONETIME = <MilestoneType.ONETIME: 2>
WEEKLY = <MilestoneType.WEEKLY: 3>
DAILY = <MilestoneType.DAILY: 4>
SPECIAL = <MilestoneType.SPECIAL: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class NotFound(aiobungie.HTTPException):
145@attrs.define(auto_exc=True)
146class NotFound(HTTPException):
147    """Raised when an unknown resource was not found."""
148
149    http_status: http.HTTPStatus = attrs.field(
150        default=http.HTTPStatus.NOT_FOUND, init=False
151    )

Raised when an unknown resource was not found.

NotFound( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class NotFound.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class ObjectiveUIStyle(builtins.int, aiobungie.Enum):
 94@typing.final
 95class ObjectiveUIStyle(int, enums.Enum):
 96    NONE = 0
 97    HIGHLIGHTED = 1
 98    CRAFTING_WEAPON_LEVEL = 2
 99    CRAFTING_WEAPON_LEVEL_PROGRESS = 3
100    CRAFTING_WEAPON_TIMESTAMP = 4
101    CRAFTING_MEMENTOS = 5
102    CRAFTING_MEMENTO_TITLE = 6

An enumeration.

NONE = <ObjectiveUIStyle.NONE: 0>
HIGHLIGHTED = <ObjectiveUIStyle.HIGHLIGHTED: 1>
CRAFTING_WEAPON_LEVEL = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL: 2>
CRAFTING_WEAPON_LEVEL_PROGRESS = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL_PROGRESS: 3>
CRAFTING_WEAPON_TIMESTAMP = <ObjectiveUIStyle.CRAFTING_WEAPON_TIMESTAMP: 4>
CRAFTING_MEMENTOS = <ObjectiveUIStyle.CRAFTING_MEMENTOS: 5>
CRAFTING_MEMENTO_TITLE = <ObjectiveUIStyle.CRAFTING_MEMENTO_TITLE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Place(builtins.int, aiobungie.Enum):
220@typing.final
221class Place(int, Enum):
222    """An Enum for Destiny 2 Places and NOT Planets"""
223
224    ORBIT = 2961497387
225    SOCIAL = 4151112093
226    LIGHT_HOUSE = 4276116472
227    EXPLORE = 3497767639

An Enum for Destiny 2 Places and NOT Planets

ORBIT = <Place.ORBIT: 2961497387>
SOCIAL = <Place.SOCIAL: 4151112093>
LIGHT_HOUSE = <Place.LIGHT_HOUSE: 4276116472>
EXPLORE = <Place.EXPLORE: 3497767639>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Planet(builtins.int, aiobungie.Enum):
185@typing.final
186class Planet(int, Enum):
187    """An Enum for all available planets in Destiny 2."""
188
189    UNKNOWN = 0
190    """Unknown space"""
191
192    EARTH = 3747705955
193    """Earth"""
194
195    DREAMING_CITY = 2877881518
196    """The Dreaming city."""
197
198    NESSUS = 3526908984
199    """Nessus"""
200
201    MOON = 3325508439
202    """The Moon"""
203
204    COSMODROME = 3990611421
205    """The Cosmodrome"""
206
207    TANGLED_SHORE = 3821439926
208    """The Tangled Shore"""
209
210    VENUS = 3871070152
211    """Venus"""
212
213    EAZ = 541863059  # Exclusive event.
214    """European Aerial Zone"""
215
216    EUROPA = 1729879943
217    """Europa"""

An Enum for all available planets in Destiny 2.

UNKNOWN = <Planet.UNKNOWN: 0>

Unknown space

EARTH = <Planet.EARTH: 3747705955>

Earth

DREAMING_CITY = <Planet.DREAMING_CITY: 2877881518>

The Dreaming city.

NESSUS = <Planet.NESSUS: 3526908984>

Nessus

MOON = <Planet.MOON: 3325508439>

The Moon

COSMODROME = <Planet.COSMODROME: 3990611421>

The Cosmodrome

TANGLED_SHORE = <Planet.TANGLED_SHORE: 3821439926>

The Tangled Shore

VENUS = <Planet.VENUS: 3871070152>

Venus

EAZ = <Planet.EAZ: 541863059>

European Aerial Zone

EUROPA = <Planet.EUROPA: 1729879943>

Europa

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Presence(builtins.int, aiobungie.Enum):
672@typing.final
673class Presence(int, Enum):
674    """An enum for a bungie friend status."""
675
676    OFFLINE_OR_UNKNOWN = 0
677    ONLINE = 1

An enum for a bungie friend status.

OFFLINE_OR_UNKNOWN = <Presence.OFFLINE_OR_UNKNOWN: 0>
ONLINE = <Presence.ONLINE: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class PrivacySetting(builtins.int, aiobungie.Enum):
760@typing.final
761class PrivacySetting(int, Enum):
762    """An enum for players's privacy settings."""
763
764    OPEN = 0
765    CLAN_AND_FRIENDS = 1
766    FRIENDS_ONLY = 2
767    INVITE_ONLY = 3
768    CLOSED = 4

An enum for players's privacy settings.

OPEN = <PrivacySetting.OPEN: 0>
CLAN_AND_FRIENDS = <PrivacySetting.CLAN_AND_FRIENDS: 1>
FRIENDS_ONLY = <PrivacySetting.FRIENDS_ONLY: 2>
INVITE_ONLY = <PrivacySetting.INVITE_ONLY: 3>
CLOSED = <PrivacySetting.CLOSED: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RESTClient(aiobungie.interfaces.rest.RESTInterface):
 309class RESTClient(interfaces.RESTInterface):
 310    """A RESTful client implementation for Bungie's API.
 311
 312    This client is designed to only make HTTP requests and return JSON objects
 313    to provide RESTful functionality.
 314
 315    This client is the core for `aiobungie.Client` which deserialize those returned JSON objects
 316    using the factory into Pythonic data classes objects which provide Python functionality.
 317
 318    Example
 319    -------
 320    ```py
 321    import aiobungie
 322
 323    client = aiobungie.RESTClient("TOKEN")
 324    async with client:
 325        response = await client.fetch_clan_members(4389205)
 326        for member in response['results']:
 327            for key, value in member['destinyUserInfo'].items():
 328                print(key, value)
 329    ```
 330
 331    Parameters
 332    ----------
 333    token : `str`
 334        A valid application token from Bungie's developer portal.
 335
 336    Other Parameters
 337    ----------------
 338    max_retries : `int`
 339        The max retries number to retry if the request hit a `5xx` status code.
 340    client_secret : `typing.Optional[str]`
 341        An optional application client secret,
 342        This is only needed if you're fetching OAuth2 tokens with this client.
 343    client_id : `typing.Optional[int]`
 344        An optional application client id,
 345        This is only needed if you're fetching OAuth2 tokens with this client.
 346    enable_debugging : `bool | str`
 347        Whether to enable logging responses or not.
 348
 349    Logging Levels
 350    --------------
 351    * `False`: This will disable logging.
 352    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
 353    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
 354    """
 355
 356    __slots__ = (
 357        "_token",
 358        "_session",
 359        "_lock",
 360        "_max_retries",
 361        "_client_secret",
 362        "_client_id",
 363        "_metadata",
 364        "_dumps",
 365        "_loads",
 366    )
 367
 368    def __init__(
 369        self,
 370        token: str,
 371        /,
 372        *,
 373        client_secret: typing.Optional[str] = None,
 374        client_id: typing.Optional[int] = None,
 375        client_session: typing.Optional[aiohttp.ClientSession] = None,
 376        dumps: typedefs.Dumps = helpers.dumps,
 377        loads: typedefs.Loads = helpers.loads,
 378        max_retries: int = 4,
 379        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 380    ) -> None:
 381        self._session: typing.Optional[aiohttp.ClientSession] = client_session
 382        self._lock: typing.Optional[asyncio.Lock] = None
 383        self._client_secret = client_secret
 384        self._client_id = client_id
 385        self._token: str = token
 386        self._max_retries = max_retries
 387        self._dumps = dumps
 388        self._loads = loads
 389        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
 390
 391        self._set_debug_level(enable_debugging)
 392
 393    @property
 394    def client_id(self) -> typing.Optional[int]:
 395        return self._client_id
 396
 397    @property
 398    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 399        return self._metadata
 400
 401    @property
 402    def is_alive(self) -> bool:
 403        return self._session is not None
 404
 405    @typing.final
 406    async def close(self) -> None:
 407        if self._session is None:
 408            raise RuntimeError("REST client is not running.")
 409
 410        await self._session.close()
 411        self._session = None
 412
 413    @typing.final
 414    def open(self) -> None:
 415        """Open a new client session. This is called internally with contextmanager usage."""
 416        if self._session:
 417            raise RuntimeError("Cannot open REST client when it's already open.")
 418
 419        self._session = aiohttp.ClientSession(
 420            connector=aiohttp.TCPConnector(),
 421            connector_owner=True,
 422            raise_for_status=False,
 423            timeout=aiohttp.ClientTimeout(total=30.0),
 424        )
 425
 426    @typing.final
 427    def enable_debugging(
 428        self,
 429        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 430        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 431        /,
 432    ) -> None:
 433        self._set_debug_level(level, file)
 434
 435    @typing.final
 436    async def static_request(
 437        self,
 438        method: typing.Union[RequestMethod, str],
 439        path: str,
 440        *,
 441        auth: typing.Optional[str] = None,
 442        json: typing.Optional[dict[str, typing.Any]] = None,
 443    ) -> ResponseSig:
 444        return await self._request(method, path, auth=auth, json=json)
 445
 446    @typing.final
 447    def build_oauth2_url(
 448        self, client_id: typing.Optional[int] = None
 449    ) -> typing.Optional[builders.OAuthURL]:
 450        client_id = client_id or self._client_id
 451        if client_id is None:
 452            return None
 453
 454        return builders.OAuthURL(client_id=client_id)
 455
 456    @staticmethod
 457    def _set_debug_level(
 458        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 459        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 460    ) -> None:
 461        file_handler = logging.FileHandler(file, mode="w") if file else None
 462        if level == "TRACE" or level == TRACE:
 463            logging.basicConfig(
 464                level=TRACE, handlers=[file_handler] if file_handler else None
 465            )
 466
 467        elif level:
 468            logging.basicConfig(
 469                level=logging.DEBUG, handlers=[file_handler] if file_handler else None
 470            )
 471
 472    async def _request(
 473        self,
 474        method: typing.Union[RequestMethod, str],
 475        route: str,
 476        *,
 477        base: bool = False,
 478        oauth2: bool = False,
 479        auth: typing.Optional[str] = None,
 480        unwrapping: typing.Literal["json", "read"] = "json",
 481        json: typing.Optional[dict[str, typing.Any] | str] = None,
 482        headers: typing.Optional[dict[str, typing.Any]] = None,
 483    ) -> ResponseSig:
 484        # This is not None when opening the client.
 485        assert self._session is not None
 486
 487        retries: int = 0
 488        headers = headers or {}
 489
 490        headers.setdefault(_USER_AGENT_HEADERS, _USER_AGENT)
 491        headers["X-API-KEY"] = self._token
 492
 493        if auth is not None:
 494            headers[_AUTH_HEADER] = f"Bearer {auth}"
 495
 496        # Handling endpoints
 497        endpoint = url.BASE
 498
 499        if not base:
 500            endpoint = endpoint + url.REST_EP
 501
 502        if oauth2:
 503            headers["Content-Type"] = "application/x-www-form-urlencoded"
 504            endpoint = endpoint + url.TOKEN_EP
 505
 506        if self._lock is None:
 507            self._lock = asyncio.Lock()
 508
 509        while True:
 510            async with (stack := contextlib.AsyncExitStack()):
 511                await stack.enter_async_context(self._lock)
 512
 513                data = self._dumps(json) if isinstance(json, dict) else json
 514                # We make the request here.
 515                taken_time = time.monotonic()
 516                response = await self._session.request(
 517                    method=method, url=f"{endpoint}/{route}", headers=headers, data=data
 518                )
 519                response_time = (time.monotonic() - taken_time) * 1_000
 520
 521                _LOG.debug(
 522                    "%s %s %s Time %.4fms",
 523                    method,
 524                    f"{endpoint}/{route}",
 525                    f"{response.status} {response.reason}",
 526                    response_time,
 527                )
 528
 529                await self._handle_ratelimit(response, method, route)
 530
 531                if response.status == http.HTTPStatus.NO_CONTENT:
 532                    return None
 533
 534                if 300 > response.status >= 200:
 535                    if unwrapping == "read":
 536                        # We need to read the bytes for the manifest response.
 537                        return await response.read()
 538
 539                    if response.content_type == _APP_JSON:
 540                        # json_data = self._loads(await response.read())
 541                        json_data = self._loads(await response.read())
 542
 543                        _LOG.debug(
 544                            "%s %s %s Time %.4fms",
 545                            method,
 546                            f"{endpoint}/{route}",
 547                            f"{response.status} {response.reason}",
 548                            response_time,
 549                        )
 550
 551                        if _LOG.isEnabledFor(TRACE):
 552                            cloned = headers.copy()
 553                            cloned.update(response.headers)  # type: ignore
 554
 555                            _LOG.log(
 556                                TRACE,
 557                                "%s",
 558                                error.stringify_http_message(cloned),
 559                            )
 560
 561                        # Return the response.
 562                        # oauth2 responses are not packed inside a Response object.
 563                        if oauth2:
 564                            return json_data  # type: ignore[no-any-return]
 565
 566                        return json_data["Response"]  # type: ignore
 567
 568                if (
 569                    response.status in _RETRY_5XX and retries < self._max_retries  # noqa: W503
 570                ):
 571                    backoff_ = backoff.ExponentialBackOff(maximum=6)
 572                    sleep_time = next(backoff_)
 573                    _LOG.warning(
 574                        "Got %i - %s. Sleeping for %.2f seconds. Remaining retries: %i",
 575                        response.status,
 576                        response.reason,
 577                        sleep_time,
 578                        self._max_retries - retries,
 579                    )
 580
 581                    retries += 1
 582                    await asyncio.sleep(sleep_time)
 583                    continue
 584
 585                raise await error.raise_error(response)
 586
 587    if not typing.TYPE_CHECKING:
 588
 589        def __enter__(self) -> typing.NoReturn:
 590            cls = type(self)
 591            raise TypeError(
 592                f"{cls.__qualname__} is async only, use 'async with' instead."
 593            )
 594
 595        def __exit__(
 596            self,
 597            exception_type: typing.Optional[type[BaseException]],
 598            exception: typing.Optional[BaseException],
 599            exception_traceback: typing.Optional[types.TracebackType],
 600        ) -> None:
 601            ...
 602
 603    async def __aenter__(self) -> RESTClient:
 604        self.open()
 605        return self
 606
 607    async def __aexit__(
 608        self,
 609        exception_type: typing.Optional[type[BaseException]],
 610        exception: typing.Optional[BaseException],
 611        exception_traceback: typing.Optional[types.TracebackType],
 612    ) -> None:
 613        await self.close()
 614
 615    # We don't want this to be super complicated.
 616    @typing.final
 617    async def _handle_ratelimit(
 618        self,
 619        response: aiohttp.ClientResponse,
 620        method: str,
 621        route: str,
 622    ) -> None:
 623        if response.status != http.HTTPStatus.TOO_MANY_REQUESTS:
 624            return
 625
 626        if response.content_type != _APP_JSON:
 627            raise error.HTTPError(
 628                f"Being ratelimited on non JSON request, {response.content_type}.",
 629                http.HTTPStatus.TOO_MANY_REQUESTS,
 630            )
 631
 632        json: typedefs.JSONObject = self._loads(await response.read())  # type: ignore
 633        retry_after = float(json.get("ThrottleSeconds", 15.0)) + 0.1
 634        max_calls: int = 0
 635
 636        while True:
 637            if max_calls == 10:
 638                # Max retries by default. We raise an error here.
 639                raise error.RateLimitedError(
 640                    body=json,
 641                    url=str(response.real_url),
 642                    retry_after=retry_after,
 643                )
 644
 645            # We sleep for a little bit to avoid funky behavior.
 646            _LOG.warning(
 647                "We're being ratelimited, Method %s Route %s. Sleeping for %.2fs.",
 648                method,
 649                route,
 650                retry_after,
 651            )
 652            await asyncio.sleep(retry_after)
 653            max_calls += 1
 654            continue
 655
 656    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
 657        if not isinstance(self._client_secret, (str, int)):
 658            raise TypeError(
 659                "Expected (str, int) for client secret "
 660                f"but got {type(self._client_secret).__name__}"  # type: ignore
 661            )
 662
 663        headers = {
 664            "client_secret": self._client_secret,
 665        }
 666
 667        data = (
 668            f"grant_type=authorization_code&code={code}"
 669            f"&client_id={self._client_id}&client_secret={self._client_secret}"
 670        )
 671
 672        response = await self._request(
 673            RequestMethod.POST, "", headers=headers, json=data, oauth2=True
 674        )
 675        assert isinstance(response, dict)
 676        return builders.OAuth2Response.build_response(response)
 677
 678    async def refresh_access_token(
 679        self, refresh_token: str, /
 680    ) -> builders.OAuth2Response:
 681        if not isinstance(self._client_secret, (int, str)):
 682            raise TypeError(
 683                f"Expected (str, int) for client secret but got {type(self._client_secret).__name__}"  # type: ignore
 684            )
 685
 686        data = {
 687            "grant_type": "refresh_token",
 688            "refresh_token": refresh_token,
 689            "client_id": self._client_id,
 690            "client_secret": self._client_secret,
 691            "Content-Type": "application/x-www-form-urlencoded",
 692        }
 693
 694        response = await self._request(RequestMethod.POST, "", json=data, oauth2=True)
 695        assert isinstance(response, dict)
 696        return builders.OAuth2Response.build_response(response)
 697
 698    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
 699        resp = await self._request(
 700            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
 701        )
 702        assert isinstance(resp, dict)
 703        return resp
 704
 705    async def fetch_user_themes(self) -> typedefs.JSONArray:
 706        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
 707        assert isinstance(resp, list)
 708        return resp
 709
 710    async def fetch_membership_from_id(
 711        self,
 712        id: int,
 713        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 714        /,
 715    ) -> typedefs.JSONObject:
 716        resp = await self._request(
 717            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
 718        )
 719        assert isinstance(resp, dict)
 720        return resp
 721
 722    async def fetch_player(
 723        self,
 724        name: str,
 725        code: int,
 726        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 727        /,
 728    ) -> typedefs.JSONArray:
 729        resp = await self._request(
 730            RequestMethod.POST,
 731            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
 732            json={"displayName": name, "displayNameCode": code},
 733        )
 734        assert isinstance(resp, list)
 735        return resp
 736
 737    async def search_users(self, name: str, /) -> typedefs.JSONObject:
 738        resp = await self._request(
 739            RequestMethod.POST,
 740            "User/Search/GlobalName/0",
 741            json={"displayNamePrefix": name},
 742        )
 743        assert isinstance(resp, dict)
 744        return resp
 745
 746    async def fetch_clan_from_id(
 747        self, id: int, /, access_token: typing.Optional[str] = None
 748    ) -> typedefs.JSONObject:
 749        resp = await self._request(
 750            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
 751        )
 752        assert isinstance(resp, dict)
 753        return resp
 754
 755    async def fetch_clan(
 756        self,
 757        name: str,
 758        /,
 759        access_token: typing.Optional[str] = None,
 760        *,
 761        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 762    ) -> typedefs.JSONObject:
 763        resp = await self._request(
 764            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
 765        )
 766        assert isinstance(resp, dict)
 767        return resp
 768
 769    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
 770        resp = await self._request(
 771            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
 772        )
 773        assert isinstance(resp, dict)
 774        return resp
 775
 776    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
 777        resp = await self._request(
 778            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
 779        )
 780        assert isinstance(resp, list)
 781        return resp
 782
 783    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
 784        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
 785        assert isinstance(resp, dict)
 786        return resp
 787
 788    async def fetch_character(
 789        self,
 790        member_id: int,
 791        membership_type: typedefs.IntAnd[enums.MembershipType],
 792        character_id: int,
 793        components: list[enums.ComponentType],
 794        auth: typing.Optional[str] = None,
 795    ) -> typedefs.JSONObject:
 796        collector = _collect_components(components)
 797        response = await self._request(
 798            RequestMethod.GET,
 799            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
 800            f"Character/{character_id}/?components={collector}",
 801            auth=auth,
 802        )
 803        assert isinstance(response, dict)
 804        return response
 805
 806    async def fetch_activities(
 807        self,
 808        member_id: int,
 809        character_id: int,
 810        mode: typedefs.IntAnd[enums.GameMode],
 811        membership_type: typedefs.IntAnd[
 812            enums.MembershipType
 813        ] = enums.MembershipType.ALL,
 814        *,
 815        page: int = 0,
 816        limit: int = 1,
 817    ) -> typedefs.JSONObject:
 818        resp = await self._request(
 819            RequestMethod.GET,
 820            f"Destiny2/{int(membership_type)}/Account/"
 821            f"{member_id}/Character/{character_id}/Stats/Activities"
 822            f"/?mode={int(mode)}&count={limit}&page={page}",
 823        )
 824        assert isinstance(resp, dict)
 825        return resp
 826
 827    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
 828        resp = await self._request(
 829            RequestMethod.GET,
 830            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
 831        )
 832        assert isinstance(resp, dict)
 833        return resp
 834
 835    async def fetch_profile(
 836        self,
 837        membership_id: int,
 838        type: typedefs.IntAnd[enums.MembershipType],
 839        components: list[enums.ComponentType],
 840        auth: typing.Optional[str] = None,
 841    ) -> typedefs.JSONObject:
 842        collector = _collect_components(components)
 843        response = await self._request(
 844            RequestMethod.GET,
 845            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
 846            auth=auth,
 847        )
 848        assert isinstance(response, dict)
 849        return response
 850
 851    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
 852        response = await self._request(
 853            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
 854        )
 855        assert isinstance(response, dict)
 856        return response
 857
 858    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
 859        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
 860        assert isinstance(resp, dict)
 861        return resp
 862
 863    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
 864        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
 865        assert isinstance(resp, dict)
 866        return resp
 867
 868    async def fetch_groups_for_member(
 869        self,
 870        member_id: int,
 871        member_type: typedefs.IntAnd[enums.MembershipType],
 872        /,
 873        *,
 874        filter: int = 0,
 875        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 876    ) -> typedefs.JSONObject:
 877        resp = await self._request(
 878            RequestMethod.GET,
 879            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 880        )
 881        assert isinstance(resp, dict)
 882        return resp
 883
 884    async def fetch_potential_groups_for_member(
 885        self,
 886        member_id: int,
 887        member_type: typedefs.IntAnd[enums.MembershipType],
 888        /,
 889        *,
 890        filter: int = 0,
 891        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 892    ) -> typedefs.JSONObject:
 893        resp = await self._request(
 894            RequestMethod.GET,
 895            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 896        )
 897        assert isinstance(resp, dict)
 898        return resp
 899
 900    async def fetch_clan_members(
 901        self,
 902        clan_id: int,
 903        /,
 904        *,
 905        name: typing.Optional[str] = None,
 906        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 907    ) -> typedefs.JSONObject:
 908        resp = await self._request(
 909            RequestMethod.GET,
 910            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
 911        )
 912        assert isinstance(resp, dict)
 913        return resp
 914
 915    async def fetch_hardlinked_credentials(
 916        self,
 917        credential: int,
 918        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
 919        /,
 920    ) -> typedefs.JSONObject:
 921        resp = await self._request(
 922            RequestMethod.GET,
 923            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
 924        )
 925        assert isinstance(resp, dict)
 926        return resp
 927
 928    async def fetch_user_credentials(
 929        self, access_token: str, membership_id: int, /
 930    ) -> typedefs.JSONArray:
 931        resp = await self._request(
 932            RequestMethod.GET,
 933            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
 934            auth=access_token,
 935        )
 936        assert isinstance(resp, list)
 937        return resp
 938
 939    async def insert_socket_plug(
 940        self,
 941        action_token: str,
 942        /,
 943        instance_id: int,
 944        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
 945        character_id: int,
 946        membership_type: typedefs.IntAnd[enums.MembershipType],
 947    ) -> typedefs.JSONObject:
 948        if isinstance(plug, builders.PlugSocketBuilder):
 949            plug = plug.collect()
 950
 951        body = {
 952            "actionToken": action_token,
 953            "itemInstanceId": instance_id,
 954            "plug": plug,
 955            "characterId": character_id,
 956            "membershipType": int(membership_type),
 957        }
 958        resp = await self._request(
 959            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
 960        )
 961        assert isinstance(resp, dict)
 962        return resp
 963
 964    async def insert_socket_plug_free(
 965        self,
 966        access_token: str,
 967        /,
 968        instance_id: int,
 969        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
 970        character_id: int,
 971        membership_type: typedefs.IntAnd[enums.MembershipType],
 972    ) -> typedefs.JSONObject:
 973        if isinstance(plug, builders.PlugSocketBuilder):
 974            plug = plug.collect()
 975
 976        body = {
 977            "itemInstanceId": instance_id,
 978            "plug": plug,
 979            "characterId": character_id,
 980            "membershipType": int(membership_type),
 981        }
 982        resp = await self._request(
 983            RequestMethod.POST,
 984            "Destiny2/Actions/Items/InsertSocketPlugFree",
 985            json=body,
 986            auth=access_token,
 987        )
 988        assert isinstance(resp, dict)
 989        return resp
 990
 991    async def set_item_lock_state(
 992        self,
 993        access_token: str,
 994        state: bool,
 995        /,
 996        item_id: int,
 997        character_id: int,
 998        membership_type: typedefs.IntAnd[enums.MembershipType],
 999    ) -> int:
1000        body = {
1001            "state": state,
1002            "itemId": item_id,
1003            "characterId": character_id,
1004            "membershipType": int(membership_type),
1005        }
1006        response = await self._request(
1007            RequestMethod.POST,
1008            "Destiny2/Actions/Items/SetLockState",
1009            json=body,
1010            auth=access_token,
1011        )
1012        assert isinstance(response, int)
1013        return response
1014
1015    async def set_quest_track_state(
1016        self,
1017        access_token: str,
1018        state: bool,
1019        /,
1020        item_id: int,
1021        character_id: int,
1022        membership_type: typedefs.IntAnd[enums.MembershipType],
1023    ) -> int:
1024        body = {
1025            "state": state,
1026            "itemId": item_id,
1027            "characterId": character_id,
1028            "membership_type": int(membership_type),
1029        }
1030        response = await self._request(
1031            RequestMethod.POST,
1032            "Destiny2/Actions/Items/SetTrackedState",
1033            json=body,
1034            auth=access_token,
1035        )
1036        assert isinstance(response, int)
1037        return response
1038
1039    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1040        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1041        assert isinstance(path, dict)
1042        return path
1043
1044    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1045        _ensure_manifest_language(language)
1046
1047        content = await self.fetch_manifest_path()
1048        resp = await self._request(
1049            RequestMethod.GET,
1050            content["mobileWorldContentPaths"][language],
1051            unwrapping="read",
1052            base=True,
1053        )
1054        assert isinstance(resp, bytes)
1055        return resp
1056
1057    async def download_manifest(
1058        self,
1059        language: str = "en",
1060        name: str = "manifest",
1061        path: typing.Union[pathlib.Path, str] = ".",
1062        *,
1063        force: bool = False,
1064    ) -> None:
1065        complete_path = _get_path(name, path, sql=True)
1066
1067        if complete_path.exists() and force:
1068            if force:
1069                _LOG.info(
1070                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1071                )
1072                complete_path.unlink(missing_ok=True)
1073
1074                return await self.download_manifest(language, name, path, force=force)
1075
1076            else:
1077                raise FileExistsError(
1078                    "Manifest file already exists, "
1079                    "To force download, set the `force` parameter to `True`."
1080                )
1081
1082        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1083        data_bytes = await self.read_manifest_bytes(language)
1084        await asyncio.get_running_loop().run_in_executor(
1085            None, _write_sqlite_bytes, data_bytes, path, name
1086        )
1087
1088    async def download_json_manifest(
1089        self,
1090        file_name: str = "manifest",
1091        path: typing.Union[str, pathlib.Path] = ".",
1092        language: str = "en",
1093    ) -> None:
1094        _ensure_manifest_language(language)
1095
1096        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1097
1098        content = await self.fetch_manifest_path()
1099        json_bytes = await self._request(
1100            RequestMethod.GET,
1101            content["jsonWorldContentPaths"][language],
1102            unwrapping="read",
1103            base=True,
1104        )
1105
1106        await asyncio.get_running_loop().run_in_executor(
1107            None, _write_json_bytes, json_bytes, file_name, path
1108        )
1109        _LOG.info("Finished downloading manifest JSON.")
1110
1111    async def fetch_manifest_version(self) -> str:
1112        return typing.cast(str, (await self.fetch_manifest_path())["version"])
1113
1114    async def fetch_linked_profiles(
1115        self,
1116        member_id: int,
1117        member_type: typedefs.IntAnd[enums.MembershipType],
1118        /,
1119        *,
1120        all: bool = False,
1121    ) -> typedefs.JSONObject:
1122        resp = await self._request(
1123            RequestMethod.GET,
1124            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1125        )
1126        assert isinstance(resp, dict)
1127        return resp
1128
1129    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1130        resp = await self._request(
1131            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1132        )
1133        assert isinstance(resp, dict)
1134        return resp
1135
1136    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1137        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1138        assert isinstance(resp, dict)
1139        return resp
1140
1141    async def fetch_public_milestone_content(
1142        self, milestone_hash: int, /
1143    ) -> typedefs.JSONObject:
1144        resp = await self._request(
1145            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1146        )
1147        assert isinstance(resp, dict)
1148        return resp
1149
1150    async def fetch_current_user_memberships(
1151        self, access_token: str, /
1152    ) -> typedefs.JSONObject:
1153        resp = await self._request(
1154            RequestMethod.GET,
1155            "User/GetMembershipsForCurrentUser/",
1156            auth=access_token,
1157        )
1158        assert isinstance(resp, dict)
1159        return resp
1160
1161    async def equip_item(
1162        self,
1163        access_token: str,
1164        /,
1165        item_id: int,
1166        character_id: int,
1167        membership_type: typedefs.IntAnd[enums.MembershipType],
1168    ) -> None:
1169        payload = {
1170            "itemId": item_id,
1171            "characterId": character_id,
1172            "membershipType": int(membership_type),
1173        }
1174
1175        await self._request(
1176            RequestMethod.POST,
1177            "Destiny2/Actions/Items/EquipItem/",
1178            json=payload,
1179            auth=access_token,
1180        )
1181
1182    async def equip_items(
1183        self,
1184        access_token: str,
1185        /,
1186        item_ids: list[int],
1187        character_id: int,
1188        membership_type: typedefs.IntAnd[enums.MembershipType],
1189    ) -> None:
1190        payload = {
1191            "itemIds": item_ids,
1192            "characterId": character_id,
1193            "membershipType": int(membership_type),
1194        }
1195        await self._request(
1196            RequestMethod.POST,
1197            "Destiny2/Actions/Items/EquipItems/",
1198            json=payload,
1199            auth=access_token,
1200        )
1201
1202    async def ban_clan_member(
1203        self,
1204        access_token: str,
1205        /,
1206        group_id: int,
1207        membership_id: int,
1208        membership_type: typedefs.IntAnd[enums.MembershipType],
1209        *,
1210        length: int = 0,
1211        comment: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1212    ) -> None:
1213        payload = {"comment": str(comment), "length": length}
1214        await self._request(
1215            RequestMethod.POST,
1216            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1217            json=payload,
1218            auth=access_token,
1219        )
1220
1221    async def unban_clan_member(
1222        self,
1223        access_token: str,
1224        /,
1225        group_id: int,
1226        membership_id: int,
1227        membership_type: typedefs.IntAnd[enums.MembershipType],
1228    ) -> None:
1229        await self._request(
1230            RequestMethod.POST,
1231            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1232            auth=access_token,
1233        )
1234
1235    async def kick_clan_member(
1236        self,
1237        access_token: str,
1238        /,
1239        group_id: int,
1240        membership_id: int,
1241        membership_type: typedefs.IntAnd[enums.MembershipType],
1242    ) -> typedefs.JSONObject:
1243        resp = await self._request(
1244            RequestMethod.POST,
1245            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1246            auth=access_token,
1247        )
1248        assert isinstance(resp, dict)
1249        return resp
1250
1251    async def edit_clan(
1252        self,
1253        access_token: str,
1254        /,
1255        group_id: int,
1256        *,
1257        name: typedefs.NoneOr[str] = None,
1258        about: typedefs.NoneOr[str] = None,
1259        motto: typedefs.NoneOr[str] = None,
1260        theme: typedefs.NoneOr[str] = None,
1261        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1262        is_public: typedefs.NoneOr[bool] = None,
1263        locale: typedefs.NoneOr[str] = None,
1264        avatar_image_index: typedefs.NoneOr[int] = None,
1265        membership_option: typedefs.NoneOr[
1266            typedefs.IntAnd[enums.MembershipOption]
1267        ] = None,
1268        allow_chat: typedefs.NoneOr[bool] = None,
1269        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1270        call_sign: typedefs.NoneOr[str] = None,
1271        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1272        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1273        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1274        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1275    ) -> None:
1276        payload = {
1277            "name": name,
1278            "about": about,
1279            "motto": motto,
1280            "theme": theme,
1281            "tags": tags,
1282            "isPublic": is_public,
1283            "avatarImageIndex": avatar_image_index,
1284            "isPublicTopicAdminOnly": is_public_topic_admin,
1285            "allowChat": allow_chat,
1286            "chatSecurity": chat_security,
1287            "callsign": call_sign,
1288            "homepage": homepage,
1289            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1290            "defaultPublicity": default_publicity,
1291            "locale": locale,
1292        }
1293        if membership_option is not None:
1294            payload["membershipOption"] = int(membership_option)
1295
1296        await self._request(
1297            RequestMethod.POST,
1298            f"GroupV2/{group_id}/Edit",
1299            json=payload,
1300            auth=access_token,
1301        )
1302
1303    async def edit_clan_options(
1304        self,
1305        access_token: str,
1306        /,
1307        group_id: int,
1308        *,
1309        invite_permissions_override: typedefs.NoneOr[bool] = None,
1310        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1311        host_guided_game_permission_override: typedefs.NoneOr[
1312            typing.Literal[0, 1, 2]
1313        ] = None,
1314        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1315        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1316    ) -> None:
1317        payload = {
1318            "InvitePermissionOverride": invite_permissions_override,
1319            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1320            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1321            "UpdateBannerPermissionOverride": update_banner_permission_override,
1322            "JoinLevel": int(join_level) if join_level else None,
1323        }
1324
1325        await self._request(
1326            RequestMethod.POST,
1327            f"GroupV2/{group_id}/EditFounderOptions",
1328            json=payload,
1329            auth=access_token,
1330        )
1331
1332    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1333        resp = await self._request(
1334            RequestMethod.GET,
1335            "Social/Friends/",
1336            auth=access_token,
1337        )
1338        assert isinstance(resp, dict)
1339        return resp
1340
1341    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1342        resp = await self._request(
1343            RequestMethod.GET,
1344            "Social/Friends/Requests",
1345            auth=access_token,
1346        )
1347        assert isinstance(resp, dict)
1348        return resp
1349
1350    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1351        await self._request(
1352            RequestMethod.POST,
1353            f"Social/Friends/Requests/Accept/{member_id}",
1354            auth=access_token,
1355        )
1356
1357    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1358        await self._request(
1359            RequestMethod.POST,
1360            f"Social/Friends/Add/{member_id}",
1361            auth=access_token,
1362        )
1363
1364    async def decline_friend_request(
1365        self, access_token: str, /, member_id: int
1366    ) -> None:
1367        await self._request(
1368            RequestMethod.POST,
1369            f"Social/Friends/Requests/Decline/{member_id}",
1370            auth=access_token,
1371        )
1372
1373    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1374        await self._request(
1375            RequestMethod.POST,
1376            f"Social/Friends/Remove/{member_id}",
1377            auth=access_token,
1378        )
1379
1380    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1381        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1382        await self._request(
1383            RequestMethod.POST,
1384            f"Social/Friends/Requests/Remove/{member_id}",
1385            auth=access_token,
1386        )
1387
1388    async def approve_all_pending_group_users(
1389        self,
1390        access_token: str,
1391        /,
1392        group_id: int,
1393        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1394    ) -> None:
1395        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1396        await self._request(
1397            RequestMethod.POST,
1398            f"GroupV2/{group_id}/Members/ApproveAll",
1399            auth=access_token,
1400            json={"message": str(message)},
1401        )
1402
1403    async def deny_all_pending_group_users(
1404        self,
1405        access_token: str,
1406        /,
1407        group_id: int,
1408        *,
1409        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1410    ) -> None:
1411        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1412        await self._request(
1413            RequestMethod.POST,
1414            f"GroupV2/{group_id}/Members/DenyAll",
1415            auth=access_token,
1416            json={"message": str(message)},
1417        )
1418
1419    async def add_optional_conversation(
1420        self,
1421        access_token: str,
1422        /,
1423        group_id: int,
1424        *,
1425        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1426        security: typing.Literal[0, 1] = 0,
1427    ) -> None:
1428        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1429        payload = {"chatName": str(name), "chatSecurity": security}
1430        await self._request(
1431            RequestMethod.POST,
1432            f"GroupV2/{group_id}/OptionalConversations/Add",
1433            json=payload,
1434            auth=access_token,
1435        )
1436
1437    async def edit_optional_conversation(
1438        self,
1439        access_token: str,
1440        /,
1441        group_id: int,
1442        conversation_id: int,
1443        *,
1444        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1445        security: typing.Literal[0, 1] = 0,
1446        enable_chat: bool = False,
1447    ) -> None:
1448        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1449        payload = {
1450            "chatEnabled": enable_chat,
1451            "chatName": str(name),
1452            "chatSecurity": security,
1453        }
1454        await self._request(
1455            RequestMethod.POST,
1456            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1457            json=payload,
1458            auth=access_token,
1459        )
1460
1461    async def transfer_item(
1462        self,
1463        access_token: str,
1464        /,
1465        item_id: int,
1466        item_hash: int,
1467        character_id: int,
1468        member_type: typedefs.IntAnd[enums.MembershipType],
1469        *,
1470        stack_size: int = 1,
1471        vault: bool = False,
1472    ) -> None:
1473        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1474        payload = {
1475            "characterId": character_id,
1476            "membershipType": int(member_type),
1477            "itemId": item_id,
1478            "itemReferenceHash": item_hash,
1479            "stackSize": stack_size,
1480            "transferToVault": vault,
1481        }
1482        await self._request(
1483            RequestMethod.POST,
1484            "Destiny2/Actions/Items/TransferItem",
1485            json=payload,
1486            auth=access_token,
1487        )
1488
1489    async def pull_item(
1490        self,
1491        access_token: str,
1492        /,
1493        item_id: int,
1494        item_hash: int,
1495        character_id: int,
1496        member_type: typedefs.IntAnd[enums.MembershipType],
1497        *,
1498        stack_size: int = 1,
1499        vault: bool = False,
1500    ) -> None:
1501        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1502        payload = {
1503            "characterId": character_id,
1504            "membershipType": int(member_type),
1505            "itemId": item_id,
1506            "itemReferenceHash": item_hash,
1507            "stackSize": stack_size,
1508            "transferToVault": vault,
1509        }
1510        await self._request(
1511            RequestMethod.POST,
1512            "Destiny2/Actions/Items/PullFromPostmaster",
1513            json=payload,
1514            auth=access_token,
1515        )
1516
1517    async def fetch_fireteams(
1518        self,
1519        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1520        *,
1521        platform: typedefs.IntAnd[
1522            fireteams.FireteamPlatform
1523        ] = fireteams.FireteamPlatform.ANY,
1524        language: typing.Union[
1525            fireteams.FireteamLanguage, str
1526        ] = fireteams.FireteamLanguage.ALL,
1527        date_range: typedefs.IntAnd[
1528            fireteams.FireteamDate
1529        ] = fireteams.FireteamDate.ALL,
1530        page: int = 0,
1531        slots_filter: int = 0,
1532    ) -> typedefs.JSONObject:
1533        resp = await self._request(
1534            RequestMethod.GET,
1535            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1536        )
1537        assert isinstance(resp, dict)
1538        return resp
1539
1540    async def fetch_available_clan_fireteams(
1541        self,
1542        access_token: str,
1543        group_id: int,
1544        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1545        *,
1546        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1547        language: typing.Union[fireteams.FireteamLanguage, str],
1548        date_range: typedefs.IntAnd[
1549            fireteams.FireteamDate
1550        ] = fireteams.FireteamDate.ALL,
1551        page: int = 0,
1552        public_only: bool = False,
1553        slots_filter: int = 0,
1554    ) -> typedefs.JSONObject:
1555        resp = await self._request(
1556            RequestMethod.GET,
1557            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1558            json={"langFilter": str(language)},
1559            auth=access_token,
1560        )
1561        assert isinstance(resp, dict)
1562        return resp
1563
1564    async def fetch_clan_fireteam(
1565        self, access_token: str, fireteam_id: int, group_id: int
1566    ) -> typedefs.JSONObject:
1567        resp = await self._request(
1568            RequestMethod.GET,
1569            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1570            auth=access_token,
1571        )
1572        assert isinstance(resp, dict)
1573        return resp
1574
1575    async def fetch_my_clan_fireteams(
1576        self,
1577        access_token: str,
1578        group_id: int,
1579        *,
1580        include_closed: bool = True,
1581        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1582        language: typing.Union[fireteams.FireteamLanguage, str],
1583        filtered: bool = True,
1584        page: int = 0,
1585    ) -> typedefs.JSONObject:
1586        payload = {"groupFilter": filtered, "langFilter": str(language)}
1587
1588        resp = await self._request(
1589            RequestMethod.GET,
1590            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1591            json=payload,
1592            auth=access_token,
1593        )
1594        assert isinstance(resp, dict)
1595        return resp
1596
1597    async def fetch_private_clan_fireteams(
1598        self, access_token: str, group_id: int, /
1599    ) -> int:
1600        resp = await self._request(
1601            RequestMethod.GET,
1602            f"Fireteam/Clan/{group_id}/ActiveCount",
1603            auth=access_token,
1604        )
1605        assert isinstance(resp, int)
1606        return resp
1607
1608    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1609        resp = await self._request(
1610            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1611        )
1612        assert isinstance(resp, dict)
1613        return resp
1614
1615    async def search_entities(
1616        self, name: str, entity_type: str, *, page: int = 0
1617    ) -> typedefs.JSONObject:
1618        resp = await self._request(
1619            RequestMethod.GET,
1620            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1621            json={"page": page},
1622        )
1623        assert isinstance(resp, dict)
1624        return resp
1625
1626    async def fetch_unique_weapon_history(
1627        self,
1628        membership_id: int,
1629        character_id: int,
1630        membership_type: typedefs.IntAnd[enums.MembershipType],
1631    ) -> typedefs.JSONObject:
1632        resp = await self._request(
1633            RequestMethod.GET,
1634            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1635        )
1636        assert isinstance(resp, dict)
1637        return resp
1638
1639    async def fetch_item(
1640        self,
1641        member_id: int,
1642        item_id: int,
1643        membership_type: typedefs.IntAnd[enums.MembershipType],
1644        components: list[enums.ComponentType],
1645    ) -> typedefs.JSONObject:
1646        collector = _collect_components(components)
1647
1648        resp = await self._request(
1649            RequestMethod.GET,
1650            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1651        )
1652        assert isinstance(resp, dict)
1653        return resp
1654
1655    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1656        resp = await self._request(
1657            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1658        )
1659        assert isinstance(resp, dict)
1660        return resp
1661
1662    async def fetch_available_locales(self) -> typedefs.JSONObject:
1663        resp = await self._request(
1664            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1665        )
1666        assert isinstance(resp, dict)
1667        return resp
1668
1669    async def fetch_common_settings(self) -> typedefs.JSONObject:
1670        resp = await self._request(RequestMethod.GET, "Settings")
1671        assert isinstance(resp, dict)
1672        return resp
1673
1674    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1675        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1676        assert isinstance(resp, dict)
1677        return resp
1678
1679    async def fetch_global_alerts(
1680        self, *, include_streaming: bool = False
1681    ) -> typedefs.JSONArray:
1682        resp = await self._request(
1683            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1684        )
1685        assert isinstance(resp, list)
1686        return resp
1687
1688    async def awainitialize_request(
1689        self,
1690        access_token: str,
1691        type: typing.Literal[0, 1],
1692        membership_type: typedefs.IntAnd[enums.MembershipType],
1693        /,
1694        *,
1695        affected_item_id: typing.Optional[int] = None,
1696        character_id: typing.Optional[int] = None,
1697    ) -> typedefs.JSONObject:
1698        body = {"type": type, "membershipType": int(membership_type)}
1699
1700        if affected_item_id is not None:
1701            body["affectedItemId"] = affected_item_id
1702
1703        if character_id is not None:
1704            body["characterId"] = character_id
1705
1706        resp = await self._request(
1707            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1708        )
1709        assert isinstance(resp, dict)
1710        return resp
1711
1712    async def awaget_action_token(
1713        self, access_token: str, correlation_id: str, /
1714    ) -> typedefs.JSONObject:
1715        resp = await self._request(
1716            RequestMethod.POST,
1717            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1718            auth=access_token,
1719        )
1720        assert isinstance(resp, dict)
1721        return resp
1722
1723    async def awa_provide_authorization_result(
1724        self,
1725        access_token: str,
1726        selection: int,
1727        correlation_id: str,
1728        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1729    ) -> int:
1730        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1731
1732        resp = await self._request(
1733            RequestMethod.POST,
1734            "Destiny2/Awa/AwaProvideAuthorizationResult",
1735            json=body,
1736            auth=access_token,
1737        )
1738        assert isinstance(resp, int)
1739        return resp
1740
1741    async def fetch_vendors(
1742        self,
1743        access_token: str,
1744        character_id: int,
1745        membership_id: int,
1746        membership_type: typedefs.IntAnd[enums.MembershipType],
1747        /,
1748        components: list[enums.ComponentType],
1749        filter: typing.Optional[int] = None,
1750    ) -> typedefs.JSONObject:
1751        components_ = _collect_components(components)
1752        route = (
1753            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1754            f"/Character/{character_id}/Vendors/?components={components_}"
1755        )
1756
1757        if filter is not None:
1758            route = route + f"&filter={filter}"
1759
1760        resp = await self._request(
1761            RequestMethod.GET,
1762            route,
1763            auth=access_token,
1764        )
1765        assert isinstance(resp, dict)
1766        return resp
1767
1768    async def fetch_vendor(
1769        self,
1770        access_token: str,
1771        character_id: int,
1772        membership_id: int,
1773        membership_type: typedefs.IntAnd[enums.MembershipType],
1774        vendor_hash: int,
1775        /,
1776        components: list[enums.ComponentType],
1777    ) -> typedefs.JSONObject:
1778        components_ = _collect_components(components)
1779        resp = await self._request(
1780            RequestMethod.GET,
1781            (
1782                f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1783                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1784            ),
1785            auth=access_token,
1786        )
1787        assert isinstance(resp, dict)
1788        return resp
1789
1790    async def fetch_application_api_usage(
1791        self,
1792        access_token: str,
1793        application_id: int,
1794        /,
1795        *,
1796        start: typing.Optional[datetime.datetime] = None,
1797        end: typing.Optional[datetime.datetime] = None,
1798    ) -> typedefs.JSONObject:
1799        end_date, start_date = time.parse_date_range(end, start)
1800        resp = await self._request(
1801            RequestMethod.GET,
1802            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1803            auth=access_token,
1804        )
1805        assert isinstance(resp, dict)
1806        return resp
1807
1808    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1809        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1810        assert isinstance(resp, list)
1811        return resp
1812
1813    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1814        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1815        assert isinstance(resp, dict)
1816        return resp
1817
1818    async def fetch_content_by_id(
1819        self, id: int, locale: str, /, *, head: bool = False
1820    ) -> typedefs.JSONObject:
1821        resp = await self._request(
1822            RequestMethod.GET,
1823            f"Content/GetContentById/{id}/{locale}/",
1824            json={"head": head},
1825        )
1826        assert isinstance(resp, dict)
1827        return resp
1828
1829    async def fetch_content_by_tag_and_type(
1830        self, locale: str, tag: str, type: str, *, head: bool = False
1831    ) -> typedefs.JSONObject:
1832        resp = await self._request(
1833            RequestMethod.GET,
1834            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1835            json={"head": head},
1836        )
1837        assert isinstance(resp, dict)
1838        return resp
1839
1840    async def search_content_with_text(
1841        self,
1842        locale: str,
1843        /,
1844        content_type: str,
1845        search_text: str,
1846        tag: str,
1847        *,
1848        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1849        source: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1850    ) -> typedefs.JSONObject:
1851        body: typedefs.JSONObject = {}
1852
1853        body["ctype"] = content_type
1854        body["searchtext"] = search_text
1855        body["tag"] = tag
1856
1857        if page is not undefined.UNDEFINED:
1858            body["currentpage"] = page
1859        else:
1860            body["currentpage"] = 1
1861
1862        if source is not undefined.UNDEFINED:
1863            body["source"] = source
1864        else:
1865            source = ""
1866        resp = await self._request(
1867            RequestMethod.GET, f"Content/Search/{locale}/", json=body
1868        )
1869        assert isinstance(resp, dict)
1870        return resp
1871
1872    async def search_content_by_tag_and_type(
1873        self,
1874        locale: str,
1875        tag: str,
1876        type: str,
1877        *,
1878        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1879    ) -> typedefs.JSONObject:
1880        body: typedefs.JSONObject = {}
1881        body["currentpage"] = 1 if page is undefined.UNDEFINED else page
1882        resp = await self._request(
1883            RequestMethod.GET,
1884            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
1885            json=body,
1886        )
1887        assert isinstance(resp, dict)
1888        return resp
1889
1890    async def search_help_articles(
1891        self, text: str, size: str, /
1892    ) -> typedefs.JSONObject:
1893        resp = await self._request(
1894            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
1895        )
1896        assert isinstance(resp, dict)
1897        return resp
1898
1899    async def fetch_topics_page(
1900        self,
1901        category_filter: int,
1902        group: int,
1903        date_filter: int,
1904        sort: typing.Union[str, bytes],
1905        *,
1906        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1907        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
1908        tag_filter: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1909    ) -> typedefs.JSONObject:
1910        body: typedefs.JSONObject = {}
1911        if locales is not undefined.UNDEFINED:
1912            body["locales"] = ",".join(str(locales))
1913        else:
1914            body["locales"] = ",".join([])
1915
1916        if tag_filter is not undefined.UNDEFINED:
1917            body["tagstring"] = tag_filter
1918        else:
1919            body["tagstring"] = ""
1920
1921        page = 0 if page is not undefined.UNDEFINED else page
1922
1923        resp = await self._request(
1924            RequestMethod.GET,
1925            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
1926            json=body,
1927        )
1928        assert isinstance(resp, dict)
1929        return resp
1930
1931    async def fetch_core_topics_page(
1932        self,
1933        category_filter: int,
1934        date_filter: int,
1935        sort: typing.Union[str, bytes],
1936        *,
1937        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1938        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
1939    ) -> typedefs.JSONObject:
1940        body: typedefs.JSONObject = {}
1941
1942        if locales is not undefined.UNDEFINED:
1943            body["locales"] = ",".join(str(locales))
1944        else:
1945            body["locales"] = ",".join([])
1946
1947        resp = await self._request(
1948            RequestMethod.GET,
1949            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.UNDEFINED else page}"
1950            f"/{sort!s}/{date_filter}/{category_filter}/",
1951            json=body,
1952        )
1953        assert isinstance(resp, dict)
1954        return resp
1955
1956    async def fetch_posts_threaded_page(
1957        self,
1958        parent_post: bool,
1959        page: int,
1960        page_size: int,
1961        parent_post_id: int,
1962        reply_size: int,
1963        root_thread_mode: bool,
1964        sort_mode: int,
1965        show_banned: typing.Optional[str] = None,
1966    ) -> typedefs.JSONObject:
1967        resp = await self._request(
1968            RequestMethod.GET,
1969            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
1970            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
1971            json={"showbanned": show_banned},
1972        )
1973        assert isinstance(resp, dict)
1974        return resp
1975
1976    async def fetch_posts_threaded_page_from_child(
1977        self,
1978        child_id: bool,
1979        page: int,
1980        page_size: int,
1981        reply_size: int,
1982        root_thread_mode: bool,
1983        sort_mode: int,
1984        show_banned: typing.Optional[str] = None,
1985    ) -> typedefs.JSONObject:
1986        resp = await self._request(
1987            RequestMethod.GET,
1988            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
1989            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
1990            json={"showbanned": show_banned},
1991        )
1992        assert isinstance(resp, dict)
1993        return resp
1994
1995    async def fetch_post_and_parent(
1996        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
1997    ) -> typedefs.JSONObject:
1998        resp = await self._request(
1999            RequestMethod.GET,
2000            f"Forum/GetPostAndParent/{child_id}/",
2001            json={"showbanned": show_banned},
2002        )
2003        assert isinstance(resp, dict)
2004        return resp
2005
2006    async def fetch_posts_and_parent_awaiting(
2007        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2008    ) -> typedefs.JSONObject:
2009        resp = await self._request(
2010            RequestMethod.GET,
2011            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2012            json={"showbanned": show_banned},
2013        )
2014        assert isinstance(resp, dict)
2015        return resp
2016
2017    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2018        resp = await self._request(
2019            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2020        )
2021        assert isinstance(resp, int)
2022        return resp
2023
2024    async def fetch_forum_tag_suggestions(
2025        self, partial_tag: str, /
2026    ) -> typedefs.JSONObject:
2027        resp = await self._request(
2028            RequestMethod.GET,
2029            "Forum/GetForumTagSuggestions/",
2030            json={"partialtag": partial_tag},
2031        )
2032        assert isinstance(resp, dict)
2033        return resp
2034
2035    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2036        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2037        assert isinstance(resp, dict)
2038        return resp
2039
2040    async def fetch_recruitment_thread_summaries(self) -> typedefs.JSONArray:
2041        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2042        assert isinstance(resp, list)
2043        return resp
2044
2045    async def fetch_recommended_groups(
2046        self,
2047        accecss_token: str,
2048        /,
2049        *,
2050        date_range: int = 0,
2051        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
2052    ) -> typedefs.JSONArray:
2053        resp = await self._request(
2054            RequestMethod.POST,
2055            f"GroupV2/Recommended/{int(group_type)}/{date_range}/",
2056            auth=accecss_token,
2057        )
2058        assert isinstance(resp, list)
2059        return resp
2060
2061    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2062        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2063        assert isinstance(resp, dict)
2064        return resp
2065
2066    async def fetch_user_clan_invite_setting(
2067        self,
2068        access_token: str,
2069        /,
2070        membership_type: typedefs.IntAnd[enums.MembershipType],
2071    ) -> bool:
2072        resp = await self._request(
2073            RequestMethod.GET,
2074            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2075            auth=access_token,
2076        )
2077        assert isinstance(resp, bool)
2078        return resp
2079
2080    async def fetch_banned_group_members(
2081        self, access_token: str, group_id: int, /, *, page: int = 1
2082    ) -> typedefs.JSONObject:
2083        resp = await self._request(
2084            RequestMethod.GET,
2085            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2086            auth=access_token,
2087        )
2088        assert isinstance(resp, dict)
2089        return resp
2090
2091    async def fetch_pending_group_memberships(
2092        self, access_token: str, group_id: int, /, *, current_page: int = 1
2093    ) -> typedefs.JSONObject:
2094        resp = await self._request(
2095            RequestMethod.GET,
2096            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2097            auth=access_token,
2098        )
2099        assert isinstance(resp, dict)
2100        return resp
2101
2102    async def fetch_invited_group_memberships(
2103        self, access_token: str, group_id: int, /, *, current_page: int = 1
2104    ) -> typedefs.JSONObject:
2105        resp = await self._request(
2106            RequestMethod.GET,
2107            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2108            auth=access_token,
2109        )
2110        assert isinstance(resp, dict)
2111        return resp
2112
2113    async def invite_member_to_group(
2114        self,
2115        access_token: str,
2116        /,
2117        group_id: int,
2118        membership_id: int,
2119        membership_type: typedefs.IntAnd[enums.MembershipType],
2120        *,
2121        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2122    ) -> typedefs.JSONObject:
2123        resp = await self._request(
2124            RequestMethod.POST,
2125            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2126            auth=access_token,
2127            json={"message": str(message)},
2128        )
2129        assert isinstance(resp, dict)
2130        return resp
2131
2132    async def cancel_group_member_invite(
2133        self,
2134        access_token: str,
2135        /,
2136        group_id: int,
2137        membership_id: int,
2138        membership_type: typedefs.IntAnd[enums.MembershipType],
2139    ) -> typedefs.JSONObject:
2140        resp = await self._request(
2141            RequestMethod.POST,
2142            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2143            auth=access_token,
2144        )
2145        assert isinstance(resp, dict)
2146        return resp
2147
2148    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2149        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2150        assert isinstance(resp, dict)
2151        return resp
2152
2153    async def fetch_historical_stats(
2154        self,
2155        character_id: int,
2156        membership_id: int,
2157        membership_type: typedefs.IntAnd[enums.MembershipType],
2158        day_start: datetime.datetime,
2159        day_end: datetime.datetime,
2160        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2161        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2162        *,
2163        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2164    ) -> typedefs.JSONObject:
2165        end, start = time.parse_date_range(day_end, day_start)
2166        resp = await self._request(
2167            RequestMethod.GET,
2168            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2169            json={
2170                "dayend": end,
2171                "daystart": start,
2172                "groups": [str(int(group)) for group in groups],
2173                "modes": [str(int(mode)) for mode in modes],
2174                "periodType": int(period_type),
2175            },
2176        )
2177        assert isinstance(resp, dict)
2178        return resp
2179
2180    async def fetch_historical_stats_for_account(
2181        self,
2182        membership_id: int,
2183        membership_type: typedefs.IntAnd[enums.MembershipType],
2184        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2185    ) -> typedefs.JSONObject:
2186        resp = await self._request(
2187            RequestMethod.GET,
2188            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2189            json={"groups": [str(int(group)) for group in groups]},
2190        )
2191        assert isinstance(resp, dict)
2192        return resp
2193
2194    async def fetch_aggregated_activity_stats(
2195        self,
2196        character_id: int,
2197        membership_id: int,
2198        membership_type: typedefs.IntAnd[enums.MembershipType],
2199        /,
2200    ) -> typedefs.JSONObject:
2201        resp = await self._request(
2202            RequestMethod.GET,
2203            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2204            f"Character/{character_id}/Stats/AggregateActivityStats/",
2205        )
2206        assert isinstance(resp, dict)
2207        return resp
2208
2209    async def equip_loadout(
2210        self,
2211        access_token: str,
2212        /,
2213        loadout_index: int,
2214        character_id: int,
2215        membership_type: typedefs.IntAnd[enums.MembershipType],
2216    ) -> None:
2217        response = await self._request(
2218            RequestMethod.POST,
2219            "Destiny2/Actions/Loadouts/EquipLoadout/",
2220            json={
2221                "loadoutIndex": loadout_index,
2222                "characterId": character_id,
2223                "membership_type": int(membership_type),
2224            },
2225            auth=access_token,
2226        )
2227        assert isinstance(response, int)
2228
2229    async def snapshot_loadout(
2230        self,
2231        access_token: str,
2232        /,
2233        loadout_index: int,
2234        character_id: int,
2235        membership_type: typedefs.IntAnd[enums.MembershipType],
2236        *,
2237        color_hash: typing.Optional[int] = None,
2238        icon_hash: typing.Optional[int] = None,
2239        name_hash: typing.Optional[int] = None,
2240    ) -> None:
2241        response = await self._request(
2242            RequestMethod.POST,
2243            "Destiny2/Actions/Loadouts/SnapshotLoadout/",
2244            auth=access_token,
2245            json={
2246                "colorHash": color_hash,
2247                "iconHash": icon_hash,
2248                "nameHash": name_hash,
2249                "loadoutIndex": loadout_index,
2250                "characterId": character_id,
2251                "membershipType": int(membership_type),
2252            },
2253        )
2254        assert isinstance(response, int)
2255
2256    async def update_loadout(
2257        self,
2258        access_token: str,
2259        /,
2260        loadout_index: int,
2261        character_id: int,
2262        membership_type: typedefs.IntAnd[enums.MembershipType],
2263        *,
2264        color_hash: typing.Optional[int] = None,
2265        icon_hash: typing.Optional[int] = None,
2266        name_hash: typing.Optional[int] = None,
2267    ) -> None:
2268        response = await self._request(
2269            RequestMethod.POST,
2270            "Destiny2/Actions/Loadouts/UpdateLoadoutIdentifiers/",
2271            auth=access_token,
2272            json={
2273                "colorHash": color_hash,
2274                "iconHash": icon_hash,
2275                "nameHash": name_hash,
2276                "loadoutIndex": loadout_index,
2277                "characterId": character_id,
2278                "membershipType": int(membership_type),
2279            },
2280        )
2281        assert isinstance(response, int)
2282
2283    async def clear_loadout(
2284        self,
2285        access_token: str,
2286        /,
2287        loadout_index: int,
2288        character_id: int,
2289        membership_type: typedefs.IntAnd[enums.MembershipType],
2290    ) -> None:
2291        response = await self._request(
2292            RequestMethod.POST,
2293            "Destiny2/Actions/Loadouts/ClearLoadout/",
2294            json={
2295                "loadoutIndex": loadout_index,
2296                "characterId": character_id,
2297                "membership_type": int(membership_type),
2298            },
2299            auth=access_token,
2300        )
2301        assert isinstance(response, int)

A RESTful client implementation for Bungie's API.

This client is designed to only make HTTP requests and return JSON objects to provide RESTful functionality.

This client is the core for aiobungie.Client which deserialize those returned JSON objects using the factory into Pythonic data classes objects which provide Python functionality.

Example
import aiobungie

client = aiobungie.RESTClient("TOKEN")
async with client:
    response = await client.fetch_clan_members(4389205)
    for member in response['results']:
        for key, value in member['destinyUserInfo'].items():
            print(key, value)
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | TRACE: This will log the response headers along with the minimal information.
RESTClient( token: str, /, *, client_secret: Optional[str] = None, client_id: Optional[int] = None, client_session: Optional[aiohttp.client.ClientSession] = None, dumps: collections.abc.Callable[[dict[str, typing.Any] | list[typing.Any]], bytes] = <function dumps>, loads: collections.abc.Callable[[str | bytes], list[typing.Any] | dict[str, typing.Any]] = <function loads>, max_retries: int = 4, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
368    def __init__(
369        self,
370        token: str,
371        /,
372        *,
373        client_secret: typing.Optional[str] = None,
374        client_id: typing.Optional[int] = None,
375        client_session: typing.Optional[aiohttp.ClientSession] = None,
376        dumps: typedefs.Dumps = helpers.dumps,
377        loads: typedefs.Loads = helpers.loads,
378        max_retries: int = 4,
379        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
380    ) -> None:
381        self._session: typing.Optional[aiohttp.ClientSession] = client_session
382        self._lock: typing.Optional[asyncio.Lock] = None
383        self._client_secret = client_secret
384        self._client_id = client_id
385        self._token: str = token
386        self._max_retries = max_retries
387        self._dumps = dumps
388        self._loads = loads
389        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
390
391        self._set_debug_level(enable_debugging)
client_id: Optional[int]

Return the client id of this REST client if provided, Otherwise None.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

This mapping is useful for storing any kind of data that the user may need.

Example
import aiobungie

client = aiobungie.RESTClient(…)

async with client:
    # Fetch auth tokens and store them
    client.metadata["tokens"] = await client.fetch_access_token("code")

# Some other time.
async with client:
    # Retrieve the tokens
    tokens: aiobungie.OAuth2Response = client.metadata["tokens"]

    # Use them to fetch your user.
    user = await client.fetch_current_user_memberships(tokens.access_token)
is_alive: bool

Returns True if the REST client is alive and False otherwise.

@typing.final
async def close(self) -> None:
405    @typing.final
406    async def close(self) -> None:
407        if self._session is None:
408            raise RuntimeError("REST client is not running.")
409
410        await self._session.close()
411        self._session = None

Close this REST client session if it was acquired.

This method is automatically called when using async with contextmanager.

Raises
  • RuntimeError: If the client is already closed.
@typing.final
def open(self) -> None:
413    @typing.final
414    def open(self) -> None:
415        """Open a new client session. This is called internally with contextmanager usage."""
416        if self._session:
417            raise RuntimeError("Cannot open REST client when it's already open.")
418
419        self._session = aiohttp.ClientSession(
420            connector=aiohttp.TCPConnector(),
421            connector_owner=True,
422            raise_for_status=False,
423            timeout=aiohttp.ClientTimeout(total=30.0),
424        )

Open a new client session. This is called internally with contextmanager usage.

@typing.final
def enable_debugging( self, level: Union[Literal['TRACE'], bool, int] = False, file: Union[pathlib.Path, str, NoneType] = None, /) -> None:
426    @typing.final
427    def enable_debugging(
428        self,
429        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
430        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
431        /,
432    ) -> None:
433        self._set_debug_level(level, file)

Enables debugging for the REST calls.

Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | TRACE: This will log the response headers along with the minimal information.
Parameters
  • level (str | bool | int): The level of debugging to enable.
  • file (pathlib.Path | str | None): The file path to write the debug logs to. If provided.
@typing.final
async def static_request( self, method: Union[RequestMethod, str], path: str, *, auth: Optional[str] = None, json: Optional[dict[str, Any]] = None) -> Union[dict[str, Any], list[Any], bytes, int, bool, NoneType]:
435    @typing.final
436    async def static_request(
437        self,
438        method: typing.Union[RequestMethod, str],
439        path: str,
440        *,
441        auth: typing.Optional[str] = None,
442        json: typing.Optional[dict[str, typing.Any]] = None,
443    ) -> ResponseSig:
444        return await self._request(method, path, auth=auth, json=json)

Perform an HTTP request given a valid Bungie endpoint.

Parameters
  • method (RequestMethod | str): The request method, This may be GET, POST, PUT, etc.
  • path (str): The Bungie endpoint or path. A path must look something like this Destiny2/3/Profile/46111239123/...
  • auth (str | None): An optional bearer token for methods that requires OAuth2 Authorization header.
  • json (dict[str, typing.Any] | None): An optional JSON data to include in the request.
Returns
  • aiobungie.rest.ResponseSig: The response payload.
@typing.final
def build_oauth2_url( self, client_id: Optional[int] = None) -> Optional[aiobungie.builders.OAuthURL]:
446    @typing.final
447    def build_oauth2_url(
448        self, client_id: typing.Optional[int] = None
449    ) -> typing.Optional[builders.OAuthURL]:
450        client_id = client_id or self._client_id
451        if client_id is None:
452            return None
453
454        return builders.OAuthURL(client_id=client_id)

Builds an OAuth2 URL using the provided user REST/Base client secret/id.

You can't get the complete string URL by using .compile() method.

Parameters
  • client_id (int | None): An optional client id to provide, If left None it will roll back to the id passed to the RESTClient, If both is None this method will return None.
Returns
async def fetch_oauth2_tokens(self, code: str, /) -> aiobungie.builders.OAuth2Response:
656    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
657        if not isinstance(self._client_secret, (str, int)):
658            raise TypeError(
659                "Expected (str, int) for client secret "
660                f"but got {type(self._client_secret).__name__}"  # type: ignore
661            )
662
663        headers = {
664            "client_secret": self._client_secret,
665        }
666
667        data = (
668            f"grant_type=authorization_code&code={code}"
669            f"&client_id={self._client_id}&client_secret={self._client_secret}"
670        )
671
672        response = await self._request(
673            RequestMethod.POST, "", headers=headers, json=data, oauth2=True
674        )
675        assert isinstance(response, dict)
676        return builders.OAuth2Response.build_response(response)

Makes a POST request and fetch the OAuth2 access_token and refresh token.

Parameters
  • code (str): The Authorization code received from the authorization endpoint found in the URL parameters.
Returns
Raises
async def refresh_access_token(self, refresh_token: str, /) -> aiobungie.builders.OAuth2Response:
678    async def refresh_access_token(
679        self, refresh_token: str, /
680    ) -> builders.OAuth2Response:
681        if not isinstance(self._client_secret, (int, str)):
682            raise TypeError(
683                f"Expected (str, int) for client secret but got {type(self._client_secret).__name__}"  # type: ignore
684            )
685
686        data = {
687            "grant_type": "refresh_token",
688            "refresh_token": refresh_token,
689            "client_id": self._client_id,
690            "client_secret": self._client_secret,
691            "Content-Type": "application/x-www-form-urlencoded",
692        }
693
694        response = await self._request(RequestMethod.POST, "", json=data, oauth2=True)
695        assert isinstance(response, dict)
696        return builders.OAuth2Response.build_response(response)

Refresh OAuth2 access token given its refresh token.

Parameters
  • refresh_token (str): The refresh token.
Returns
async def fetch_bungie_user(self, id: int) -> dict[str, typing.Any]:
698    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
699        resp = await self._request(
700            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
701        )
702        assert isinstance(resp, dict)
703        return resp

Fetch a Bungie user by their id.

Parameters
  • id (int): The user id.
Returns
Raises
async def fetch_user_themes(self) -> list[typing.Any]:
705    async def fetch_user_themes(self) -> typedefs.JSONArray:
706        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
707        assert isinstance(resp, list)
708        return resp

Fetch all available user themes.

Returns
async def fetch_membership_from_id( self, id: int, type: Union[int, MembershipType] = <MembershipType.NONE: 0>, /) -> dict[str, typing.Any]:
710    async def fetch_membership_from_id(
711        self,
712        id: int,
713        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
714        /,
715    ) -> typedefs.JSONObject:
716        resp = await self._request(
717            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
718        )
719        assert isinstance(resp, dict)
720        return resp

Fetch Bungie user's memberships from their id.

Parameters
Returns
Raises
async def fetch_player( self, name: str, code: int, type: Union[int, MembershipType] = <MembershipType.ALL: -1>, /) -> list[typing.Any]:
722    async def fetch_player(
723        self,
724        name: str,
725        code: int,
726        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
727        /,
728    ) -> typedefs.JSONArray:
729        resp = await self._request(
730            RequestMethod.POST,
731            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
732            json={"displayName": name, "displayNameCode": code},
733        )
734        assert isinstance(resp, list)
735        return resp

Fetch a Destiny 2 Player.

Parameters
Returns
Raises
async def search_users(self, name: str, /) -> dict[str, typing.Any]:
737    async def search_users(self, name: str, /) -> typedefs.JSONObject:
738        resp = await self._request(
739            RequestMethod.POST,
740            "User/Search/GlobalName/0",
741            json={"displayNamePrefix": name},
742        )
743        assert isinstance(resp, dict)
744        return resp

Search for users by their global name and return all users who share this name.

Parameters
  • name (str): The user name.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> dict[str, typing.Any]:
746    async def fetch_clan_from_id(
747        self, id: int, /, access_token: typing.Optional[str] = None
748    ) -> typedefs.JSONObject:
749        resp = await self._request(
750            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
751        )
752        assert isinstance(resp, dict)
753        return resp

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Other Parameters
  • access_token (typing.Optional[str]): An optional access token to make the request with.

    If the token was bound to a member of the clan, This field aiobungie.crates.Clan.current_user_membership will be available and will return the membership of the user who made this request.

Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
755    async def fetch_clan(
756        self,
757        name: str,
758        /,
759        access_token: typing.Optional[str] = None,
760        *,
761        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
762    ) -> typedefs.JSONObject:
763        resp = await self._request(
764            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
765        )
766        assert isinstance(resp, dict)
767        return resp

Fetch a Clan by its name. This method will return the first clan found with given name name.

Parameters
  • name (str): The clan name.
Other Parameters
Returns
Raises
async def fetch_clan_admins(self, clan_id: int, /) -> dict[str, typing.Any]:
769    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
770        resp = await self._request(
771            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
772        )
773        assert isinstance(resp, dict)
774        return resp

Fetch the admins and founder members of the clan.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_clan_conversations(self, clan_id: int, /) -> list[typing.Any]:
776    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
777        resp = await self._request(
778            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
779        )
780        assert isinstance(resp, list)
781        return resp

Fetch a clan's conversations.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_application(self, appid: int, /) -> dict[str, typing.Any]:
783    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
784        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
785        assert isinstance(resp, dict)
786        return resp

Fetch a Bungie Application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_character( self, member_id: int, membership_type: Union[int, MembershipType], character_id: int, components: list[ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
788    async def fetch_character(
789        self,
790        member_id: int,
791        membership_type: typedefs.IntAnd[enums.MembershipType],
792        character_id: int,
793        components: list[enums.ComponentType],
794        auth: typing.Optional[str] = None,
795    ) -> typedefs.JSONObject:
796        collector = _collect_components(components)
797        response = await self._request(
798            RequestMethod.GET,
799            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
800            f"Character/{character_id}/?components={collector}",
801            auth=auth,
802        )
803        assert isinstance(response, dict)
804        return response

Fetch a Destiny 2 player's characters.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, GameMode], membership_type: Union[int, MembershipType] = <MembershipType.ALL: -1>, *, page: int = 0, limit: int = 1) -> dict[str, typing.Any]:
806    async def fetch_activities(
807        self,
808        member_id: int,
809        character_id: int,
810        mode: typedefs.IntAnd[enums.GameMode],
811        membership_type: typedefs.IntAnd[
812            enums.MembershipType
813        ] = enums.MembershipType.ALL,
814        *,
815        page: int = 0,
816        limit: int = 1,
817    ) -> typedefs.JSONObject:
818        resp = await self._request(
819            RequestMethod.GET,
820            f"Destiny2/{int(membership_type)}/Account/"
821            f"{member_id}/Character/{character_id}/Stats/Activities"
822            f"/?mode={int(mode)}&count={limit}&page={page}",
823        )
824        assert isinstance(resp, dict)
825        return resp

Fetch a Destiny 2 activity for the specified user id and character.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve.
  • mode (aiobungie.typedefs.IntAnd[aiobungie.GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (aiobungie.typedefs.IntAnd[MembershipType]): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default to 1
  • limit (int): Limit the returned result. Default to 1
Returns
Raises
async def fetch_vendor_sales(self) -> dict[str, typing.Any]:
827    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
828        resp = await self._request(
829            RequestMethod.GET,
830            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
831        )
832        assert isinstance(resp, dict)
833        return resp
async def fetch_profile( self, membership_id: int, type: Union[int, MembershipType], components: list[ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
835    async def fetch_profile(
836        self,
837        membership_id: int,
838        type: typedefs.IntAnd[enums.MembershipType],
839        components: list[enums.ComponentType],
840        auth: typing.Optional[str] = None,
841    ) -> typedefs.JSONObject:
842        collector = _collect_components(components)
843        response = await self._request(
844            RequestMethod.GET,
845            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
846            auth=auth,
847        )
848        assert isinstance(response, dict)
849        return response

Fetch a bungie profile.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_entity(self, type: str, hash: int) -> dict[str, typing.Any]:
851    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
852        response = await self._request(
853            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
854        )
855        assert isinstance(response, dict)
856        return response

Fetch a Destiny definition item given its type and hash.

Parameters
  • type (str): Entity's type definition.
  • hash (int): Entity's hash.
Returns
async def fetch_inventory_item(self, hash: int, /) -> dict[str, typing.Any]:
858    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
859        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
860        assert isinstance(resp, dict)
861        return resp

Fetch a Destiny inventory item entity given a its hash.

Parameters
  • hash (int): Entity's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> dict[str, typing.Any]:
863    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
864        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
865        assert isinstance(resp, dict)
866        return resp

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, MembershipType], /, *, filter: int = 0, group_type: Union[int, GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
868    async def fetch_groups_for_member(
869        self,
870        member_id: int,
871        member_type: typedefs.IntAnd[enums.MembershipType],
872        /,
873        *,
874        filter: int = 0,
875        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
876    ) -> typedefs.JSONObject:
877        resp = await self._request(
878            RequestMethod.GET,
879            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
880        )
881        assert isinstance(resp, dict)
882        return resp

Fetch the information about the groups for a member.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, MembershipType], /, *, filter: int = 0, group_type: Union[int, GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
884    async def fetch_potential_groups_for_member(
885        self,
886        member_id: int,
887        member_type: typedefs.IntAnd[enums.MembershipType],
888        /,
889        *,
890        filter: int = 0,
891        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
892    ) -> typedefs.JSONObject:
893        resp = await self._request(
894            RequestMethod.GET,
895            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
896        )
897        assert isinstance(resp, dict)
898        return resp

Get information about the groups that a given member has applied to or been invited to.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, MembershipType] = <MembershipType.NONE: 0>) -> dict[str, typing.Any]:
900    async def fetch_clan_members(
901        self,
902        clan_id: int,
903        /,
904        *,
905        name: typing.Optional[str] = None,
906        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
907    ) -> typedefs.JSONObject:
908        resp = await self._request(
909            RequestMethod.GET,
910            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
911        )
912        assert isinstance(resp, dict)
913        return resp

Fetch all Bungie Clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
Returns
Raises
async def fetch_hardlinked_credentials( self, credential: int, type: Union[int, CredentialType] = <CredentialType.STEAMID: 12>, /) -> dict[str, typing.Any]:
915    async def fetch_hardlinked_credentials(
916        self,
917        credential: int,
918        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
919        /,
920    ) -> typedefs.JSONObject:
921        resp = await self._request(
922            RequestMethod.GET,
923            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
924        )
925        assert isinstance(resp, dict)
926        return resp

Gets any hard linked membership given a credential.

Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
Returns
async def fetch_user_credentials(self, access_token: str, membership_id: int, /) -> list[typing.Any]:
928    async def fetch_user_credentials(
929        self, access_token: str, membership_id: int, /
930    ) -> typedefs.JSONArray:
931        resp = await self._request(
932            RequestMethod.GET,
933            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
934            auth=access_token,
935        )
936        assert isinstance(resp, list)
937        return resp

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def insert_socket_plug( self, action_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, MembershipType]) -> dict[str, typing.Any]:
939    async def insert_socket_plug(
940        self,
941        action_token: str,
942        /,
943        instance_id: int,
944        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
945        character_id: int,
946        membership_type: typedefs.IntAnd[enums.MembershipType],
947    ) -> typedefs.JSONObject:
948        if isinstance(plug, builders.PlugSocketBuilder):
949            plug = plug.collect()
950
951        body = {
952            "actionToken": action_token,
953            "itemInstanceId": instance_id,
954            "plug": plug,
955            "characterId": character_id,
956            "membershipType": int(membership_type),
957        }
958        resp = await self._request(
959            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
960        )
961        assert isinstance(resp, dict)
962        return resp

Insert a plug into a socketed item.

OAuth2: AdvancedWriteActions scope is required

Parameters
  • action_token (str): Action token provided by the AwaGetActionToken API call.
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def insert_socket_plug_free( self, access_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, MembershipType]) -> dict[str, typing.Any]:
964    async def insert_socket_plug_free(
965        self,
966        access_token: str,
967        /,
968        instance_id: int,
969        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
970        character_id: int,
971        membership_type: typedefs.IntAnd[enums.MembershipType],
972    ) -> typedefs.JSONObject:
973        if isinstance(plug, builders.PlugSocketBuilder):
974            plug = plug.collect()
975
976        body = {
977            "itemInstanceId": instance_id,
978            "plug": plug,
979            "characterId": character_id,
980            "membershipType": int(membership_type),
981        }
982        resp = await self._request(
983            RequestMethod.POST,
984            "Destiny2/Actions/Items/InsertSocketPlugFree",
985            json=body,
986            auth=access_token,
987        )
988        assert isinstance(resp, dict)
989        return resp

Insert a plug into a socketed item. This doesn't require an Action token.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def set_item_lock_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, MembershipType]) -> int:
 991    async def set_item_lock_state(
 992        self,
 993        access_token: str,
 994        state: bool,
 995        /,
 996        item_id: int,
 997        character_id: int,
 998        membership_type: typedefs.IntAnd[enums.MembershipType],
 999    ) -> int:
1000        body = {
1001            "state": state,
1002            "itemId": item_id,
1003            "characterId": character_id,
1004            "membershipType": int(membership_type),
1005        }
1006        response = await self._request(
1007            RequestMethod.POST,
1008            "Destiny2/Actions/Items/SetLockState",
1009            json=body,
1010            auth=access_token,
1011        )
1012        assert isinstance(response, int)
1013        return response

Set the Lock State for an instanced item.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def set_quest_track_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, MembershipType]) -> int:
1015    async def set_quest_track_state(
1016        self,
1017        access_token: str,
1018        state: bool,
1019        /,
1020        item_id: int,
1021        character_id: int,
1022        membership_type: typedefs.IntAnd[enums.MembershipType],
1023    ) -> int:
1024        body = {
1025            "state": state,
1026            "itemId": item_id,
1027            "characterId": character_id,
1028            "membership_type": int(membership_type),
1029        }
1030        response = await self._request(
1031            RequestMethod.POST,
1032            "Destiny2/Actions/Items/SetTrackedState",
1033            json=body,
1034            auth=access_token,
1035        )
1036        assert isinstance(response, int)
1037        return response

Set the Tracking State for an instanced Quest or Bounty.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def fetch_manifest_path(self) -> dict[str, typing.Any]:
1039    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1040        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1041        assert isinstance(path, dict)
1042        return path

Fetch the manifest JSON paths.

Returns
  • typedefs.JSONObject: The manifest JSON paths.
async def read_manifest_bytes(self, language: str = 'en', /) -> bytes:
1044    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1045        _ensure_manifest_language(language)
1046
1047        content = await self.fetch_manifest_path()
1048        resp = await self._request(
1049            RequestMethod.GET,
1050            content["mobileWorldContentPaths"][language],
1051            unwrapping="read",
1052            base=True,
1053        )
1054        assert isinstance(resp, bytes)
1055        return resp

Read raw manifest SQLite database bytes response.

This method can be used to write the bytes to zipped file and then extract it to get the manifest content.

Parameters
  • language (str): The manifest database language bytes to get.
Returns
  • bytes: The bytes to read and write the manifest database.
async def download_manifest( self, language: str = 'en', name: str = 'manifest', path: Union[pathlib.Path, str] = '.', *, force: bool = False) -> None:
1057    async def download_manifest(
1058        self,
1059        language: str = "en",
1060        name: str = "manifest",
1061        path: typing.Union[pathlib.Path, str] = ".",
1062        *,
1063        force: bool = False,
1064    ) -> None:
1065        complete_path = _get_path(name, path, sql=True)
1066
1067        if complete_path.exists() and force:
1068            if force:
1069                _LOG.info(
1070                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1071                )
1072                complete_path.unlink(missing_ok=True)
1073
1074                return await self.download_manifest(language, name, path, force=force)
1075
1076            else:
1077                raise FileExistsError(
1078                    "Manifest file already exists, "
1079                    "To force download, set the `force` parameter to `True`."
1080                )
1081
1082        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1083        data_bytes = await self.read_manifest_bytes(language)
1084        await asyncio.get_running_loop().run_in_executor(
1085            None, _write_sqlite_bytes, data_bytes, path, name
1086        )

A helper method to download the manifest.

Note

This method downloads the sqlite database and not JSON. Use RESTInterface.download_json_manifest for the JSON version.

Parameters
  • language (str): The manifest language to download, Default is english.
  • path (str | pathlib.Path): The path to save the manifest sqlite database. Example "D:/", Default is the current directory.
  • name (str): The manifest database file name. Default is manifest
  • force (bool): Whether to force the download. Default is False. However if set to true the old file will get removed and a new one will being to download.
Returns
  • None
Raises
  • FileNotFoundError: If the manifest file exists and force is False.
  • ValueError: If the provided language was not recognized.
async def download_json_manifest( self, file_name: str = 'manifest', path: Union[str, pathlib.Path] = '.', language: str = 'en') -> None:
1088    async def download_json_manifest(
1089        self,
1090        file_name: str = "manifest",
1091        path: typing.Union[str, pathlib.Path] = ".",
1092        language: str = "en",
1093    ) -> None:
1094        _ensure_manifest_language(language)
1095
1096        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1097
1098        content = await self.fetch_manifest_path()
1099        json_bytes = await self._request(
1100            RequestMethod.GET,
1101            content["jsonWorldContentPaths"][language],
1102            unwrapping="read",
1103            base=True,
1104        )
1105
1106        await asyncio.get_running_loop().run_in_executor(
1107            None, _write_json_bytes, json_bytes, file_name, path
1108        )
1109        _LOG.info("Finished downloading manifest JSON.")

Download the Bungie manifest json file.

Parameters
  • file_name (str): The file name to save the manifest json file. Default is manifest.
  • path (str | pathlib.Path): The path to save the manifest json file. Default is the current directory. Example "D:/"
  • language (str): The manifest database language bytes to get. Default is English.
async def fetch_manifest_version(self) -> str:
1111    async def fetch_manifest_version(self) -> str:
1112        return typing.cast(str, (await self.fetch_manifest_path())["version"])

Fetch the manifest version.

Returns
  • str: The manifest version.
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, MembershipType], /, *, all: bool = False) -> dict[str, typing.Any]:
1114    async def fetch_linked_profiles(
1115        self,
1116        member_id: int,
1117        member_type: typedefs.IntAnd[enums.MembershipType],
1118        /,
1119        *,
1120        all: bool = False,
1121    ) -> typedefs.JSONObject:
1122        resp = await self._request(
1123            RequestMethod.GET,
1124            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1125        )
1126        assert isinstance(resp, dict)
1127        return resp

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
async def fetch_clan_banners(self) -> dict[str, typing.Any]:
1129    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1130        resp = await self._request(
1131            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1132        )
1133        assert isinstance(resp, dict)
1134        return resp

Fetch the values of the clan banners.

Returns
async def fetch_public_milestones(self) -> dict[str, typing.Any]:
1136    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1137        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1138        assert isinstance(resp, dict)
1139        return resp

Fetch the available milestones.

Returns
async def fetch_public_milestone_content(self, milestone_hash: int, /) -> dict[str, typing.Any]:
1141    async def fetch_public_milestone_content(
1142        self, milestone_hash: int, /
1143    ) -> typedefs.JSONObject:
1144        resp = await self._request(
1145            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1146        )
1147        assert isinstance(resp, dict)
1148        return resp

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
async def fetch_current_user_memberships(self, access_token: str, /) -> dict[str, typing.Any]:
1150    async def fetch_current_user_memberships(
1151        self, access_token: str, /
1152    ) -> typedefs.JSONObject:
1153        resp = await self._request(
1154            RequestMethod.GET,
1155            "User/GetMembershipsForCurrentUser/",
1156            auth=access_token,
1157        )
1158        assert isinstance(resp, dict)
1159        return resp

Fetch a bungie user's accounts with the signed in user. This GET method requires a Bearer access token for the authorization.

This requires OAuth2 scope enabled and the valid Bearer access_token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def equip_item( self, access_token: str, /, item_id: int, character_id: int, membership_type: Union[int, MembershipType]) -> None:
1161    async def equip_item(
1162        self,
1163        access_token: str,
1164        /,
1165        item_id: int,
1166        character_id: int,
1167        membership_type: typedefs.IntAnd[enums.MembershipType],
1168    ) -> None:
1169        payload = {
1170            "itemId": item_id,
1171            "characterId": character_id,
1172            "membershipType": int(membership_type),
1173        }
1174
1175        await self._request(
1176            RequestMethod.POST,
1177            "Destiny2/Actions/Items/EquipItem/",
1178            json=payload,
1179            auth=access_token,
1180        )

Equip an item to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item id.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type associated with this player.
async def equip_items( self, access_token: str, /, item_ids: list[int], character_id: int, membership_type: Union[int, MembershipType]) -> None:
1182    async def equip_items(
1183        self,
1184        access_token: str,
1185        /,
1186        item_ids: list[int],
1187        character_id: int,
1188        membership_type: typedefs.IntAnd[enums.MembershipType],
1189    ) -> None:
1190        payload = {
1191            "itemIds": item_ids,
1192            "characterId": character_id,
1193            "membershipType": int(membership_type),
1194        }
1195        await self._request(
1196            RequestMethod.POST,
1197            "Destiny2/Actions/Items/EquipItems/",
1198            json=payload,
1199            auth=access_token,
1200        )

Equip multiple items to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_ids (list[int]): A list of item ids.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type associated with this player.
async def ban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, MembershipType], *, length: int = 0, comment: Union[UndefinedType, str] = UNDEFINED) -> None:
1202    async def ban_clan_member(
1203        self,
1204        access_token: str,
1205        /,
1206        group_id: int,
1207        membership_id: int,
1208        membership_type: typedefs.IntAnd[enums.MembershipType],
1209        *,
1210        length: int = 0,
1211        comment: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1212    ) -> None:
1213        payload = {"comment": str(comment), "length": length}
1214        await self._request(
1215            RequestMethod.POST,
1216            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1217            json=payload,
1218            auth=access_token,
1219        )

Bans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to ban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Other Parameters
async def unban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, MembershipType]) -> None:
1221    async def unban_clan_member(
1222        self,
1223        access_token: str,
1224        /,
1225        group_id: int,
1226        membership_id: int,
1227        membership_type: typedefs.IntAnd[enums.MembershipType],
1228    ) -> None:
1229        await self._request(
1230            RequestMethod.POST,
1231            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1232            auth=access_token,
1233        )

Unban a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to unban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, MembershipType]) -> dict[str, typing.Any]:
1235    async def kick_clan_member(
1236        self,
1237        access_token: str,
1238        /,
1239        group_id: int,
1240        membership_id: int,
1241        membership_type: typedefs.IntAnd[enums.MembershipType],
1242    ) -> typedefs.JSONObject:
1243        resp = await self._request(
1244            RequestMethod.POST,
1245            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1246            auth=access_token,
1247        )
1248        assert isinstance(resp, dict)
1249        return resp

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
async def edit_clan( self, access_token: str, /, group_id: int, *, name: Optional[str] = None, about: Optional[str] = None, motto: Optional[str] = None, theme: Optional[str] = None, tags: Optional[collections.abc.Sequence[str]] = None, is_public: Optional[bool] = None, locale: Optional[str] = None, avatar_image_index: Optional[int] = None, membership_option: Union[NoneType, int, MembershipOption] = None, allow_chat: Optional[bool] = None, chat_security: Optional[Literal[0, 1]] = None, call_sign: Optional[str] = None, homepage: Optional[Literal[0, 1, 2]] = None, enable_invite_messaging_for_admins: Optional[bool] = None, default_publicity: Optional[Literal[0, 1, 2]] = None, is_public_topic_admin: Optional[bool] = None) -> None:
1251    async def edit_clan(
1252        self,
1253        access_token: str,
1254        /,
1255        group_id: int,
1256        *,
1257        name: typedefs.NoneOr[str] = None,
1258        about: typedefs.NoneOr[str] = None,
1259        motto: typedefs.NoneOr[str] = None,
1260        theme: typedefs.NoneOr[str] = None,
1261        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1262        is_public: typedefs.NoneOr[bool] = None,
1263        locale: typedefs.NoneOr[str] = None,
1264        avatar_image_index: typedefs.NoneOr[int] = None,
1265        membership_option: typedefs.NoneOr[
1266            typedefs.IntAnd[enums.MembershipOption]
1267        ] = None,
1268        allow_chat: typedefs.NoneOr[bool] = None,
1269        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1270        call_sign: typedefs.NoneOr[str] = None,
1271        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1272        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1273        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1274        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1275    ) -> None:
1276        payload = {
1277            "name": name,
1278            "about": about,
1279            "motto": motto,
1280            "theme": theme,
1281            "tags": tags,
1282            "isPublic": is_public,
1283            "avatarImageIndex": avatar_image_index,
1284            "isPublicTopicAdminOnly": is_public_topic_admin,
1285            "allowChat": allow_chat,
1286            "chatSecurity": chat_security,
1287            "callsign": call_sign,
1288            "homepage": homepage,
1289            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1290            "defaultPublicity": default_publicity,
1291            "locale": locale,
1292        }
1293        if membership_option is not None:
1294            payload["membershipOption"] = int(membership_option)
1295
1296        await self._request(
1297            RequestMethod.POST,
1298            f"GroupV2/{group_id}/Edit",
1299            json=payload,
1300            auth=access_token,
1301        )

Edit a clan.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id to edit.
Other Parameters
async def edit_clan_options( self, access_token: str, /, group_id: int, *, invite_permissions_override: Optional[bool] = None, update_culture_permissionOverride: Optional[bool] = None, host_guided_game_permission_override: Optional[Literal[0, 1, 2]] = None, update_banner_permission_override: Optional[bool] = None, join_level: Union[NoneType, int, ClanMemberType] = None) -> None:
1303    async def edit_clan_options(
1304        self,
1305        access_token: str,
1306        /,
1307        group_id: int,
1308        *,
1309        invite_permissions_override: typedefs.NoneOr[bool] = None,
1310        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1311        host_guided_game_permission_override: typedefs.NoneOr[
1312            typing.Literal[0, 1, 2]
1313        ] = None,
1314        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1315        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1316    ) -> None:
1317        payload = {
1318            "InvitePermissionOverride": invite_permissions_override,
1319            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1320            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1321            "UpdateBannerPermissionOverride": update_banner_permission_override,
1322            "JoinLevel": int(join_level) if join_level else None,
1323        }
1324
1325        await self._request(
1326            RequestMethod.POST,
1327            f"GroupV2/{group_id}/EditFounderOptions",
1328            json=payload,
1329            auth=access_token,
1330        )

Edit the clan options.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
Other Parameters
  • invite_permissions_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to invite new members to group Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • update_culture_permissionOverride (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update group culture Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • host_guided_game_permission_override (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): Minimum Member Level allowed to host guided games Always Allowed: Founder, Acting Founder, Admin Allowed Overrides: 0 -> None, 1 -> Beginner 2 -> Member. Default is Member for clans, None for groups, although this means nothing for groups.
  • update_banner_permission_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update banner Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • join_level (aiobungie.ClanMemberType): Level to join a member at when accepting an invite, application, or joining an open clan. Default is aiobungie.ClanMemberType.BEGINNER
async def fetch_friends(self, access_token: str, /) -> dict[str, typing.Any]:
1332    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1333        resp = await self._request(
1334            RequestMethod.GET,
1335            "Social/Friends/",
1336            auth=access_token,
1337        )
1338        assert isinstance(resp, dict)
1339        return resp

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> dict[str, typing.Any]:
1341    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1342        resp = await self._request(
1343            RequestMethod.GET,
1344            "Social/Friends/Requests",
1345            auth=access_token,
1346        )
1347        assert isinstance(resp, dict)
1348        return resp

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1350    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1351        await self._request(
1352            RequestMethod.POST,
1353            f"Social/Friends/Requests/Accept/{member_id}",
1354            auth=access_token,
1355        )

Accepts a friend relationship with the target user. The user must be on your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to accept.
async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1357    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1358        await self._request(
1359            RequestMethod.POST,
1360            f"Social/Friends/Add/{member_id}",
1361            auth=access_token,
1362        )

Requests a friend relationship with the target user.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to send the request to.
async def decline_friend_request(self, access_token: str, /, member_id: int) -> None:
1364    async def decline_friend_request(
1365        self, access_token: str, /, member_id: int
1366    ) -> None:
1367        await self._request(
1368            RequestMethod.POST,
1369            f"Social/Friends/Requests/Decline/{member_id}",
1370            auth=access_token,
1371        )

Decline a friend request with the target user. The user must be in your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to decline.
async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1373    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1374        await self._request(
1375            RequestMethod.POST,
1376            f"Social/Friends/Remove/{member_id}",
1377            auth=access_token,
1378        )

Removes a friend from your friend list. The user must be in your friend list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove.
async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1380    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1381        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1382        await self._request(
1383            RequestMethod.POST,
1384            f"Social/Friends/Requests/Remove/{member_id}",
1385            auth=access_token,
1386        )

Removes a friend from your friend list requests. The user must be in your outgoing request list.

.. note : This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove from the requested friend list.
async def approve_all_pending_group_users( self, access_token: str, /, group_id: int, message: Union[UndefinedType, str] = UNDEFINED) -> None:
1388    async def approve_all_pending_group_users(
1389        self,
1390        access_token: str,
1391        /,
1392        group_id: int,
1393        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1394    ) -> None:
1395        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1396        await self._request(
1397            RequestMethod.POST,
1398            f"GroupV2/{group_id}/Members/ApproveAll",
1399            auth=access_token,
1400            json={"message": str(message)},
1401        )

Approve all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
async def deny_all_pending_group_users( self, access_token: str, /, group_id: int, *, message: Union[UndefinedType, str] = UNDEFINED) -> None:
1403    async def deny_all_pending_group_users(
1404        self,
1405        access_token: str,
1406        /,
1407        group_id: int,
1408        *,
1409        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1410    ) -> None:
1411        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1412        await self._request(
1413            RequestMethod.POST,
1414            f"GroupV2/{group_id}/Members/DenyAll",
1415            auth=access_token,
1416            json={"message": str(message)},
1417        )

Deny all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
async def add_optional_conversation( self, access_token: str, /, group_id: int, *, name: Union[UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0) -> None:
1419    async def add_optional_conversation(
1420        self,
1421        access_token: str,
1422        /,
1423        group_id: int,
1424        *,
1425        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1426        security: typing.Literal[0, 1] = 0,
1427    ) -> None:
1428        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1429        payload = {"chatName": str(name), "chatSecurity": security}
1430        await self._request(
1431            RequestMethod.POST,
1432            f"GroupV2/{group_id}/OptionalConversations/Add",
1433            json=payload,
1434            auth=access_token,
1435        )

Add a new chat channel to a group.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other parameters

name: aiobungie.UndefinedOr[str] The chat name. Default to UNDEFINED security: typing.Literal[0, 1] The security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`
async def edit_optional_conversation( self, access_token: str, /, group_id: int, conversation_id: int, *, name: Union[UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0, enable_chat: bool = False) -> None:
1437    async def edit_optional_conversation(
1438        self,
1439        access_token: str,
1440        /,
1441        group_id: int,
1442        conversation_id: int,
1443        *,
1444        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1445        security: typing.Literal[0, 1] = 0,
1446        enable_chat: bool = False,
1447    ) -> None:
1448        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1449        payload = {
1450            "chatEnabled": enable_chat,
1451            "chatName": str(name),
1452            "chatSecurity": security,
1453        }
1454        await self._request(
1455            RequestMethod.POST,
1456            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1457            json=payload,
1458            auth=access_token,
1459        )

Edit the settings of this chat channel.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
  • conversation_id (int): The conversation/chat id.
Other parameters

name: aiobungie.UndefinedOr[str] The new chat name. Default to UNDEFINED security: typing.Literal[0, 1] The new security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`

enable_chat : bool Whether to enable chatting or not. If set to True then chatting will be enabled. Otherwise it will be disabled.

async def transfer_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1461    async def transfer_item(
1462        self,
1463        access_token: str,
1464        /,
1465        item_id: int,
1466        item_hash: int,
1467        character_id: int,
1468        member_type: typedefs.IntAnd[enums.MembershipType],
1469        *,
1470        stack_size: int = 1,
1471        vault: bool = False,
1472    ) -> None:
1473        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1474        payload = {
1475            "characterId": character_id,
1476            "membershipType": int(member_type),
1477            "itemId": item_id,
1478            "itemReferenceHash": item_hash,
1479            "stackSize": stack_size,
1480            "transferToVault": vault,
1481        }
1482        await self._request(
1483            RequestMethod.POST,
1484            "Destiny2/Actions/Items/TransferItem",
1485            json=payload,
1486            auth=access_token,
1487        )

Transfer an item from / to your vault.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id you to transfer.
  • item_hash (int): The item hash.
  • character_id (int): The character id to transfer the item from/to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • vault (bool): Whether to transfer this item to your vault or not. Defaults to False.
async def pull_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1489    async def pull_item(
1490        self,
1491        access_token: str,
1492        /,
1493        item_id: int,
1494        item_hash: int,
1495        character_id: int,
1496        member_type: typedefs.IntAnd[enums.MembershipType],
1497        *,
1498        stack_size: int = 1,
1499        vault: bool = False,
1500    ) -> None:
1501        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1502        payload = {
1503            "characterId": character_id,
1504            "membershipType": int(member_type),
1505            "itemId": item_id,
1506            "itemReferenceHash": item_hash,
1507            "stackSize": stack_size,
1508            "transferToVault": vault,
1509        }
1510        await self._request(
1511            RequestMethod.POST,
1512            "Destiny2/Actions/Items/PullFromPostmaster",
1513            json=payload,
1514            auth=access_token,
1515        )

pull an item from the postmaster.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id to pull.
  • item_hash (int): The item hash.
  • character_id (int): The character id to pull the item to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • vault (bool): Whether to pill this item to your vault or not. Defaults to False.
async def fetch_fireteams( self, activity_type: Union[int, FireteamActivity], *, platform: Union[int, FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: Union[int, FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, slots_filter: int = 0) -> dict[str, typing.Any]:
1517    async def fetch_fireteams(
1518        self,
1519        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1520        *,
1521        platform: typedefs.IntAnd[
1522            fireteams.FireteamPlatform
1523        ] = fireteams.FireteamPlatform.ANY,
1524        language: typing.Union[
1525            fireteams.FireteamLanguage, str
1526        ] = fireteams.FireteamLanguage.ALL,
1527        date_range: typedefs.IntAnd[
1528            fireteams.FireteamDate
1529        ] = fireteams.FireteamDate.ALL,
1530        page: int = 0,
1531        slots_filter: int = 0,
1532    ) -> typedefs.JSONObject:
1533        resp = await self._request(
1534            RequestMethod.GET,
1535            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1536        )
1537        assert isinstance(resp, dict)
1538        return resp

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.typedefs.IntAnd[aiobungie.FireteamDate]): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_available_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, FireteamActivity], *, platform: Union[int, FireteamPlatform], language: Union[FireteamLanguage, str], date_range: Union[int, FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> dict[str, typing.Any]:
1540    async def fetch_available_clan_fireteams(
1541        self,
1542        access_token: str,
1543        group_id: int,
1544        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1545        *,
1546        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1547        language: typing.Union[fireteams.FireteamLanguage, str],
1548        date_range: typedefs.IntAnd[
1549            fireteams.FireteamDate
1550        ] = fireteams.FireteamDate.ALL,
1551        page: int = 0,
1552        public_only: bool = False,
1553        slots_filter: int = 0,
1554    ) -> typedefs.JSONObject:
1555        resp = await self._request(
1556            RequestMethod.GET,
1557            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1558            json={"langFilter": str(language)},
1559            auth=access_token,
1560        )
1561        assert isinstance(resp, dict)
1562        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.typedefs.IntAnd[aiobungie.FireteamDate]): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> dict[str, typing.Any]:
1564    async def fetch_clan_fireteam(
1565        self, access_token: str, fireteam_id: int, group_id: int
1566    ) -> typedefs.JSONObject:
1567        resp = await self._request(
1568            RequestMethod.GET,
1569            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1570            auth=access_token,
1571        )
1572        assert isinstance(resp, dict)
1573        return resp

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, FireteamPlatform], language: Union[FireteamLanguage, str], filtered: bool = True, page: int = 0) -> dict[str, typing.Any]:
1575    async def fetch_my_clan_fireteams(
1576        self,
1577        access_token: str,
1578        group_id: int,
1579        *,
1580        include_closed: bool = True,
1581        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1582        language: typing.Union[fireteams.FireteamLanguage, str],
1583        filtered: bool = True,
1584        page: int = 0,
1585    ) -> typedefs.JSONObject:
1586        payload = {"groupFilter": filtered, "langFilter": str(language)}
1587
1588        resp = await self._request(
1589            RequestMethod.GET,
1590            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1591            json=payload,
1592            auth=access_token,
1593        )
1594        assert isinstance(resp, dict)
1595        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_private_clan_fireteams(self, access_token: str, group_id: int, /) -> int:
1597    async def fetch_private_clan_fireteams(
1598        self, access_token: str, group_id: int, /
1599    ) -> int:
1600        resp = await self._request(
1601            RequestMethod.GET,
1602            f"Fireteam/Clan/{group_id}/ActiveCount",
1603            auth=access_token,
1604        )
1605        assert isinstance(resp, int)
1606        return resp

Fetch the active count of the clan fireteams that are only private.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id.
Returns
  • int: The active fireteams count. Max value returned is 25.
async def fetch_post_activity(self, instance_id: int, /) -> dict[str, typing.Any]:
1608    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1609        resp = await self._request(
1610            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1611        )
1612        assert isinstance(resp, dict)
1613        return resp

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> dict[str, typing.Any]:
1615    async def search_entities(
1616        self, name: str, entity_type: str, *, page: int = 0
1617    ) -> typedefs.JSONObject:
1618        resp = await self._request(
1619            RequestMethod.GET,
1620            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1621            json={"page": page},
1622        )
1623        assert isinstance(resp, dict)
1624        return resp

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, MembershipType]) -> dict[str, typing.Any]:
1626    async def fetch_unique_weapon_history(
1627        self,
1628        membership_id: int,
1629        character_id: int,
1630        membership_type: typedefs.IntAnd[enums.MembershipType],
1631    ) -> typedefs.JSONObject:
1632        resp = await self._request(
1633            RequestMethod.GET,
1634            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1635        )
1636        assert isinstance(resp, dict)
1637        return resp

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_item( self, member_id: int, item_id: int, membership_type: Union[int, MembershipType], components: list[ComponentType]) -> dict[str, typing.Any]:
1639    async def fetch_item(
1640        self,
1641        member_id: int,
1642        item_id: int,
1643        membership_type: typedefs.IntAnd[enums.MembershipType],
1644        components: list[enums.ComponentType],
1645    ) -> typedefs.JSONObject:
1646        collector = _collect_components(components)
1647
1648        resp = await self._request(
1649            RequestMethod.GET,
1650            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1651        )
1652        assert isinstance(resp, dict)
1653        return resp

Fetch an instanced Destiny 2 item's details.

Parameters
Returns
async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> dict[str, typing.Any]:
1655    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1656        resp = await self._request(
1657            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1658        )
1659        assert isinstance(resp, dict)
1660        return resp

Fetch the weekly reward state for a clan.

Parameters
  • clan_id (int): The clan id.
Returns
async def fetch_available_locales(self) -> dict[str, typing.Any]:
1662    async def fetch_available_locales(self) -> typedefs.JSONObject:
1663        resp = await self._request(
1664            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1665        )
1666        assert isinstance(resp, dict)
1667        return resp

Fetch available locales at Bungie.

Returns
async def fetch_common_settings(self) -> dict[str, typing.Any]:
1669    async def fetch_common_settings(self) -> typedefs.JSONObject:
1670        resp = await self._request(RequestMethod.GET, "Settings")
1671        assert isinstance(resp, dict)
1672        return resp

Fetch the common settings used by Bungie's envirotment.

Returns
async def fetch_user_systems_overrides(self) -> dict[str, typing.Any]:
1674    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1675        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1676        assert isinstance(resp, dict)
1677        return resp

Fetch a user's specific system overrides.

Returns
async def fetch_global_alerts(self, *, include_streaming: bool = False) -> list[typing.Any]:
1679    async def fetch_global_alerts(
1680        self, *, include_streaming: bool = False
1681    ) -> typedefs.JSONArray:
1682        resp = await self._request(
1683            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1684        )
1685        assert isinstance(resp, list)
1686        return resp

Fetch any active global alerts.

Parameters
  • include_streaming (bool): If True, the returned results will include streaming alerts. Default is False.
Returns
async def awainitialize_request( self, access_token: str, type: Literal[0, 1], membership_type: Union[int, MembershipType], /, *, affected_item_id: Optional[int] = None, character_id: Optional[int] = None) -> dict[str, typing.Any]:
1688    async def awainitialize_request(
1689        self,
1690        access_token: str,
1691        type: typing.Literal[0, 1],
1692        membership_type: typedefs.IntAnd[enums.MembershipType],
1693        /,
1694        *,
1695        affected_item_id: typing.Optional[int] = None,
1696        character_id: typing.Optional[int] = None,
1697    ) -> typedefs.JSONObject:
1698        body = {"type": type, "membershipType": int(membership_type)}
1699
1700        if affected_item_id is not None:
1701            body["affectedItemId"] = affected_item_id
1702
1703        if character_id is not None:
1704            body["characterId"] = character_id
1705
1706        resp = await self._request(
1707            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1708        )
1709        assert isinstance(resp, dict)
1710        return resp

Initialize a request to perform an advanced write action.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • type (typing.Literal[0, 1]): Type of the advanced write action. Its either 0 or 1. If set to 0 that means it None. Otherwise if 1 that means its insert plugs.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type of the account to modify.
Other Parameters
  • affected_item_id (typing.Optional[int]): Item instance ID the action shall be applied to. This is optional for all but a new AwaType values.
  • character_id (typing.Optional[int]): The Destiny character ID to perform this action on.
Returns
async def awaget_action_token(self, access_token: str, correlation_id: str, /) -> dict[str, typing.Any]:
1712    async def awaget_action_token(
1713        self, access_token: str, correlation_id: str, /
1714    ) -> typedefs.JSONObject:
1715        resp = await self._request(
1716            RequestMethod.POST,
1717            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1718            auth=access_token,
1719        )
1720        assert isinstance(resp, dict)
1721        return resp

Returns the action token if user approves the request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • correlation_id (str): The identifier for the advanced write action request.
Returns
async def awa_provide_authorization_result( self, access_token: str, selection: int, correlation_id: str, nonce: collections.abc.MutableSequence[typing.Union[str, bytes]]) -> int:
1723    async def awa_provide_authorization_result(
1724        self,
1725        access_token: str,
1726        selection: int,
1727        correlation_id: str,
1728        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1729    ) -> int:
1730        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1731
1732        resp = await self._request(
1733            RequestMethod.POST,
1734            "Destiny2/Awa/AwaProvideAuthorizationResult",
1735            json=body,
1736            auth=access_token,
1737        )
1738        assert isinstance(resp, int)
1739        return resp

Provide the result of the user interaction. Called by the Bungie Destiny App to approve or reject a request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • selection (int): Indication of the selection the user has made (Approving or rejecting the action)
  • correlation_id (str): Correlation ID of the request.
  • nonce (collections.MutableSequence[str, bytes]): Secret nonce received via the PUSH notification.
Returns
  • int: ...
async def fetch_vendors( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, MembershipType], /, components: list[ComponentType], filter: Optional[int] = None) -> dict[str, typing.Any]:
1741    async def fetch_vendors(
1742        self,
1743        access_token: str,
1744        character_id: int,
1745        membership_id: int,
1746        membership_type: typedefs.IntAnd[enums.MembershipType],
1747        /,
1748        components: list[enums.ComponentType],
1749        filter: typing.Optional[int] = None,
1750    ) -> typedefs.JSONObject:
1751        components_ = _collect_components(components)
1752        route = (
1753            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1754            f"/Character/{character_id}/Vendors/?components={components_}"
1755        )
1756
1757        if filter is not None:
1758            route = route + f"&filter={filter}"
1759
1760        resp = await self._request(
1761            RequestMethod.GET,
1762            route,
1763            auth=access_token,
1764        )
1765        assert isinstance(resp, dict)
1766        return resp

Get currently available vendors from the list of vendors that can possibly have rotating inventory.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Other Parameters
  • filter (int): Filters the type of items returned from the vendor. This can be left to None.
Returns
async def fetch_vendor( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, MembershipType], vendor_hash: int, /, components: list[ComponentType]) -> dict[str, typing.Any]:
1768    async def fetch_vendor(
1769        self,
1770        access_token: str,
1771        character_id: int,
1772        membership_id: int,
1773        membership_type: typedefs.IntAnd[enums.MembershipType],
1774        vendor_hash: int,
1775        /,
1776        components: list[enums.ComponentType],
1777    ) -> typedefs.JSONObject:
1778        components_ = _collect_components(components)
1779        resp = await self._request(
1780            RequestMethod.GET,
1781            (
1782                f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1783                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1784            ),
1785            auth=access_token,
1786        )
1787        assert isinstance(resp, dict)
1788        return resp

Fetch details for a specific vendor.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • vendor_hash (int): The vendor hash to return the details for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Returns
async def fetch_application_api_usage( self, access_token: str, application_id: int, /, *, start: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None) -> dict[str, typing.Any]:
1790    async def fetch_application_api_usage(
1791        self,
1792        access_token: str,
1793        application_id: int,
1794        /,
1795        *,
1796        start: typing.Optional[datetime.datetime] = None,
1797        end: typing.Optional[datetime.datetime] = None,
1798    ) -> typedefs.JSONObject:
1799        end_date, start_date = time.parse_date_range(end, start)
1800        resp = await self._request(
1801            RequestMethod.GET,
1802            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1803            auth=access_token,
1804        )
1805        assert isinstance(resp, dict)
1806        return resp

Fetch a Bungie application's API usage.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • application_id (int): The application id to get.
Other Parameters
  • start (typing.Optional[datetime.datetime]): A datetime object can be used to collect the start of the application usage. This is limited and can go back to 30 days maximum.

    If this is left to None. It will return the last 24 hours.

  • end (typing.Optional[datetime.datetime]): A datetime object can be used to collect the end of the application usage.

    If this is left to None. It will return now.

Example
import datetime

# Fetch data from 2021 Dec 10th to 2021 Dec 20th
await fetch_application_api_usage(
    start=datetime.datetime(2021, 12, 10), end=datetime.datetime(2021, 12, 20)
)
Returns
async def fetch_bungie_applications(self) -> list[typing.Any]:
1808    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1809        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1810        assert isinstance(resp, list)
1811        return resp

Fetch details for applications created by Bungie.

Returns
async def fetch_content_type(self, type: str, /) -> dict[str, typing.Any]:
1813    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1814        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1815        assert isinstance(resp, dict)
1816        return resp
async def fetch_content_by_id( self, id: int, locale: str, /, *, head: bool = False) -> dict[str, typing.Any]:
1818    async def fetch_content_by_id(
1819        self, id: int, locale: str, /, *, head: bool = False
1820    ) -> typedefs.JSONObject:
1821        resp = await self._request(
1822            RequestMethod.GET,
1823            f"Content/GetContentById/{id}/{locale}/",
1824            json={"head": head},
1825        )
1826        assert isinstance(resp, dict)
1827        return resp
async def fetch_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, head: bool = False) -> dict[str, typing.Any]:
1829    async def fetch_content_by_tag_and_type(
1830        self, locale: str, tag: str, type: str, *, head: bool = False
1831    ) -> typedefs.JSONObject:
1832        resp = await self._request(
1833            RequestMethod.GET,
1834            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1835            json={"head": head},
1836        )
1837        assert isinstance(resp, dict)
1838        return resp
async def search_content_with_text( self, locale: str, /, content_type: str, search_text: str, tag: str, *, page: Union[UndefinedType, int] = UNDEFINED, source: Union[UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
1840    async def search_content_with_text(
1841        self,
1842        locale: str,
1843        /,
1844        content_type: str,
1845        search_text: str,
1846        tag: str,
1847        *,
1848        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1849        source: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1850    ) -> typedefs.JSONObject:
1851        body: typedefs.JSONObject = {}
1852
1853        body["ctype"] = content_type
1854        body["searchtext"] = search_text
1855        body["tag"] = tag
1856
1857        if page is not undefined.UNDEFINED:
1858            body["currentpage"] = page
1859        else:
1860            body["currentpage"] = 1
1861
1862        if source is not undefined.UNDEFINED:
1863            body["source"] = source
1864        else:
1865            source = ""
1866        resp = await self._request(
1867            RequestMethod.GET, f"Content/Search/{locale}/", json=body
1868        )
1869        assert isinstance(resp, dict)
1870        return resp
async def search_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, page: Union[UndefinedType, int] = UNDEFINED) -> dict[str, typing.Any]:
1872    async def search_content_by_tag_and_type(
1873        self,
1874        locale: str,
1875        tag: str,
1876        type: str,
1877        *,
1878        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1879    ) -> typedefs.JSONObject:
1880        body: typedefs.JSONObject = {}
1881        body["currentpage"] = 1 if page is undefined.UNDEFINED else page
1882        resp = await self._request(
1883            RequestMethod.GET,
1884            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
1885            json=body,
1886        )
1887        assert isinstance(resp, dict)
1888        return resp
async def search_help_articles(self, text: str, size: str, /) -> dict[str, typing.Any]:
1890    async def search_help_articles(
1891        self, text: str, size: str, /
1892    ) -> typedefs.JSONObject:
1893        resp = await self._request(
1894            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
1895        )
1896        assert isinstance(resp, dict)
1897        return resp
async def fetch_topics_page( self, category_filter: int, group: int, date_filter: int, sort: Union[str, bytes], *, page: Union[UndefinedType, int] = UNDEFINED, locales: Union[UndefinedType, collections.abc.Iterable[str]] = UNDEFINED, tag_filter: Union[UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
1899    async def fetch_topics_page(
1900        self,
1901        category_filter: int,
1902        group: int,
1903        date_filter: int,
1904        sort: typing.Union[str, bytes],
1905        *,
1906        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1907        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
1908        tag_filter: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1909    ) -> typedefs.JSONObject:
1910        body: typedefs.JSONObject = {}
1911        if locales is not undefined.UNDEFINED:
1912            body["locales"] = ",".join(str(locales))
1913        else:
1914            body["locales"] = ",".join([])
1915
1916        if tag_filter is not undefined.UNDEFINED:
1917            body["tagstring"] = tag_filter
1918        else:
1919            body["tagstring"] = ""
1920
1921        page = 0 if page is not undefined.UNDEFINED else page
1922
1923        resp = await self._request(
1924            RequestMethod.GET,
1925            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
1926            json=body,
1927        )
1928        assert isinstance(resp, dict)
1929        return resp
async def fetch_core_topics_page( self, category_filter: int, date_filter: int, sort: Union[str, bytes], *, page: Union[UndefinedType, int] = UNDEFINED, locales: Union[UndefinedType, collections.abc.Iterable[str]] = UNDEFINED) -> dict[str, typing.Any]:
1931    async def fetch_core_topics_page(
1932        self,
1933        category_filter: int,
1934        date_filter: int,
1935        sort: typing.Union[str, bytes],
1936        *,
1937        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1938        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
1939    ) -> typedefs.JSONObject:
1940        body: typedefs.JSONObject = {}
1941
1942        if locales is not undefined.UNDEFINED:
1943            body["locales"] = ",".join(str(locales))
1944        else:
1945            body["locales"] = ",".join([])
1946
1947        resp = await self._request(
1948            RequestMethod.GET,
1949            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.UNDEFINED else page}"
1950            f"/{sort!s}/{date_filter}/{category_filter}/",
1951            json=body,
1952        )
1953        assert isinstance(resp, dict)
1954        return resp
async def fetch_posts_threaded_page( self, parent_post: bool, page: int, page_size: int, parent_post_id: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
1956    async def fetch_posts_threaded_page(
1957        self,
1958        parent_post: bool,
1959        page: int,
1960        page_size: int,
1961        parent_post_id: int,
1962        reply_size: int,
1963        root_thread_mode: bool,
1964        sort_mode: int,
1965        show_banned: typing.Optional[str] = None,
1966    ) -> typedefs.JSONObject:
1967        resp = await self._request(
1968            RequestMethod.GET,
1969            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
1970            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
1971            json={"showbanned": show_banned},
1972        )
1973        assert isinstance(resp, dict)
1974        return resp
async def fetch_posts_threaded_page_from_child( self, child_id: bool, page: int, page_size: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
1976    async def fetch_posts_threaded_page_from_child(
1977        self,
1978        child_id: bool,
1979        page: int,
1980        page_size: int,
1981        reply_size: int,
1982        root_thread_mode: bool,
1983        sort_mode: int,
1984        show_banned: typing.Optional[str] = None,
1985    ) -> typedefs.JSONObject:
1986        resp = await self._request(
1987            RequestMethod.GET,
1988            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
1989            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
1990            json={"showbanned": show_banned},
1991        )
1992        assert isinstance(resp, dict)
1993        return resp
async def fetch_post_and_parent( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
1995    async def fetch_post_and_parent(
1996        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
1997    ) -> typedefs.JSONObject:
1998        resp = await self._request(
1999            RequestMethod.GET,
2000            f"Forum/GetPostAndParent/{child_id}/",
2001            json={"showbanned": show_banned},
2002        )
2003        assert isinstance(resp, dict)
2004        return resp
async def fetch_posts_and_parent_awaiting( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2006    async def fetch_posts_and_parent_awaiting(
2007        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2008    ) -> typedefs.JSONObject:
2009        resp = await self._request(
2010            RequestMethod.GET,
2011            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2012            json={"showbanned": show_banned},
2013        )
2014        assert isinstance(resp, dict)
2015        return resp
async def fetch_topic_for_content(self, content_id: int, /) -> int:
2017    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2018        resp = await self._request(
2019            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2020        )
2021        assert isinstance(resp, int)
2022        return resp
async def fetch_forum_tag_suggestions(self, partial_tag: str, /) -> dict[str, typing.Any]:
2024    async def fetch_forum_tag_suggestions(
2025        self, partial_tag: str, /
2026    ) -> typedefs.JSONObject:
2027        resp = await self._request(
2028            RequestMethod.GET,
2029            "Forum/GetForumTagSuggestions/",
2030            json={"partialtag": partial_tag},
2031        )
2032        assert isinstance(resp, dict)
2033        return resp
async def fetch_poll(self, topic_id: int, /) -> dict[str, typing.Any]:
2035    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2036        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2037        assert isinstance(resp, dict)
2038        return resp
async def fetch_recruitment_thread_summaries(self) -> list[typing.Any]:
2040    async def fetch_recruitment_thread_summaries(self) -> typedefs.JSONArray:
2041        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2042        assert isinstance(resp, list)
2043        return resp
async def fetch_available_avatars(self) -> collections.abc.Mapping[str, int]:
2061    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2062        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2063        assert isinstance(resp, dict)
2064        return resp
async def fetch_user_clan_invite_setting( self, access_token: str, /, membership_type: Union[int, MembershipType]) -> bool:
2066    async def fetch_user_clan_invite_setting(
2067        self,
2068        access_token: str,
2069        /,
2070        membership_type: typedefs.IntAnd[enums.MembershipType],
2071    ) -> bool:
2072        resp = await self._request(
2073            RequestMethod.GET,
2074            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2075            auth=access_token,
2076        )
2077        assert isinstance(resp, bool)
2078        return resp
async def fetch_banned_group_members( self, access_token: str, group_id: int, /, *, page: int = 1) -> dict[str, typing.Any]:
2080    async def fetch_banned_group_members(
2081        self, access_token: str, group_id: int, /, *, page: int = 1
2082    ) -> typedefs.JSONObject:
2083        resp = await self._request(
2084            RequestMethod.GET,
2085            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2086            auth=access_token,
2087        )
2088        assert isinstance(resp, dict)
2089        return resp
async def fetch_pending_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2091    async def fetch_pending_group_memberships(
2092        self, access_token: str, group_id: int, /, *, current_page: int = 1
2093    ) -> typedefs.JSONObject:
2094        resp = await self._request(
2095            RequestMethod.GET,
2096            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2097            auth=access_token,
2098        )
2099        assert isinstance(resp, dict)
2100        return resp
async def fetch_invited_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2102    async def fetch_invited_group_memberships(
2103        self, access_token: str, group_id: int, /, *, current_page: int = 1
2104    ) -> typedefs.JSONObject:
2105        resp = await self._request(
2106            RequestMethod.GET,
2107            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2108            auth=access_token,
2109        )
2110        assert isinstance(resp, dict)
2111        return resp
async def invite_member_to_group( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, MembershipType], *, message: Union[UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
2113    async def invite_member_to_group(
2114        self,
2115        access_token: str,
2116        /,
2117        group_id: int,
2118        membership_id: int,
2119        membership_type: typedefs.IntAnd[enums.MembershipType],
2120        *,
2121        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2122    ) -> typedefs.JSONObject:
2123        resp = await self._request(
2124            RequestMethod.POST,
2125            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2126            auth=access_token,
2127            json={"message": str(message)},
2128        )
2129        assert isinstance(resp, dict)
2130        return resp
async def cancel_group_member_invite( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, MembershipType]) -> dict[str, typing.Any]:
2132    async def cancel_group_member_invite(
2133        self,
2134        access_token: str,
2135        /,
2136        group_id: int,
2137        membership_id: int,
2138        membership_type: typedefs.IntAnd[enums.MembershipType],
2139    ) -> typedefs.JSONObject:
2140        resp = await self._request(
2141            RequestMethod.POST,
2142            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2143            auth=access_token,
2144        )
2145        assert isinstance(resp, dict)
2146        return resp
async def fetch_historical_definition(self) -> dict[str, typing.Any]:
2148    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2149        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2150        assert isinstance(resp, dict)
2151        return resp
async def fetch_historical_stats( self, character_id: int, membership_id: int, membership_type: Union[int, MembershipType], day_start: datetime.datetime, day_end: datetime.datetime, groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]], modes: collections.abc.Sequence[typing.Union[int, GameMode]], *, period_type: aiobungie.internal.enums.PeriodType = <PeriodType.ALL_TIME: 2>) -> dict[str, typing.Any]:
2153    async def fetch_historical_stats(
2154        self,
2155        character_id: int,
2156        membership_id: int,
2157        membership_type: typedefs.IntAnd[enums.MembershipType],
2158        day_start: datetime.datetime,
2159        day_end: datetime.datetime,
2160        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2161        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2162        *,
2163        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2164    ) -> typedefs.JSONObject:
2165        end, start = time.parse_date_range(day_end, day_start)
2166        resp = await self._request(
2167            RequestMethod.GET,
2168            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2169            json={
2170                "dayend": end,
2171                "daystart": start,
2172                "groups": [str(int(group)) for group in groups],
2173                "modes": [str(int(mode)) for mode in modes],
2174                "periodType": int(period_type),
2175            },
2176        )
2177        assert isinstance(resp, dict)
2178        return resp

Fetch historical stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • day_start (datetime.datetime): The start of the day to return the stats for.
  • day_end (datetime.datetime): The end of the day to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
  • modes (list[aiobungie.GameMode | int]): A list of game modes to return.
  • period_type (aiobungie.enums.PeriodType): The period type to return the stats for. This will return ALL_TIME by default if not modified.
Returns
async def fetch_historical_stats_for_account( self, membership_id: int, membership_type: Union[int, MembershipType], groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]]) -> dict[str, typing.Any]:
2180    async def fetch_historical_stats_for_account(
2181        self,
2182        membership_id: int,
2183        membership_type: typedefs.IntAnd[enums.MembershipType],
2184        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2185    ) -> typedefs.JSONObject:
2186        resp = await self._request(
2187            RequestMethod.GET,
2188            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2189            json={"groups": [str(int(group)) for group in groups]},
2190        )
2191        assert isinstance(resp, dict)
2192        return resp

Fetch historical stats for an account's membership.

Parameters
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, MembershipType], /) -> dict[str, typing.Any]:
2194    async def fetch_aggregated_activity_stats(
2195        self,
2196        character_id: int,
2197        membership_id: int,
2198        membership_type: typedefs.IntAnd[enums.MembershipType],
2199        /,
2200    ) -> typedefs.JSONObject:
2201        resp = await self._request(
2202            RequestMethod.GET,
2203            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2204            f"Character/{character_id}/Stats/AggregateActivityStats/",
2205        )
2206        assert isinstance(resp, dict)
2207        return resp

Fetch aggregated activity stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
Returns
async def equip_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: Union[int, MembershipType]) -> None:
2209    async def equip_loadout(
2210        self,
2211        access_token: str,
2212        /,
2213        loadout_index: int,
2214        character_id: int,
2215        membership_type: typedefs.IntAnd[enums.MembershipType],
2216    ) -> None:
2217        response = await self._request(
2218            RequestMethod.POST,
2219            "Destiny2/Actions/Loadouts/EquipLoadout/",
2220            json={
2221                "loadoutIndex": loadout_index,
2222                "characterId": character_id,
2223                "membership_type": int(membership_type),
2224            },
2225            auth=access_token,
2226        )
2227        assert isinstance(response, int)

Equip a loadout. Your character must be in a Social space, Orbit or Offline while performing this operation.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
async def snapshot_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: Union[int, MembershipType], *, color_hash: Optional[int] = None, icon_hash: Optional[int] = None, name_hash: Optional[int] = None) -> None:
2229    async def snapshot_loadout(
2230        self,
2231        access_token: str,
2232        /,
2233        loadout_index: int,
2234        character_id: int,
2235        membership_type: typedefs.IntAnd[enums.MembershipType],
2236        *,
2237        color_hash: typing.Optional[int] = None,
2238        icon_hash: typing.Optional[int] = None,
2239        name_hash: typing.Optional[int] = None,
2240    ) -> None:
2241        response = await self._request(
2242            RequestMethod.POST,
2243            "Destiny2/Actions/Loadouts/SnapshotLoadout/",
2244            auth=access_token,
2245            json={
2246                "colorHash": color_hash,
2247                "iconHash": icon_hash,
2248                "nameHash": name_hash,
2249                "loadoutIndex": loadout_index,
2250                "characterId": character_id,
2251                "membershipType": int(membership_type),
2252            },
2253        )
2254        assert isinstance(response, int)

Snapshot a loadout with the currently equipped items.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
Other Parameters
  • color_hash (int | None): ...
  • icon_hash (int | None): ...
  • name_hash (int | None): ...
async def update_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: Union[int, MembershipType], *, color_hash: Optional[int] = None, icon_hash: Optional[int] = None, name_hash: Optional[int] = None) -> None:
2256    async def update_loadout(
2257        self,
2258        access_token: str,
2259        /,
2260        loadout_index: int,
2261        character_id: int,
2262        membership_type: typedefs.IntAnd[enums.MembershipType],
2263        *,
2264        color_hash: typing.Optional[int] = None,
2265        icon_hash: typing.Optional[int] = None,
2266        name_hash: typing.Optional[int] = None,
2267    ) -> None:
2268        response = await self._request(
2269            RequestMethod.POST,
2270            "Destiny2/Actions/Loadouts/UpdateLoadoutIdentifiers/",
2271            auth=access_token,
2272            json={
2273                "colorHash": color_hash,
2274                "iconHash": icon_hash,
2275                "nameHash": name_hash,
2276                "loadoutIndex": loadout_index,
2277                "characterId": character_id,
2278                "membershipType": int(membership_type),
2279            },
2280        )
2281        assert isinstance(response, int)

Update the loadout. Color, Icon and Name.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
Other Parameters
  • color_hash (int | None): The new color hash of the loadout to update.
  • icon_hash (int | None): The new icon hash of the loadout to update.
  • name_hash (int | None): The new name hash of the loadout to update.
async def clear_loadout( self, access_token: str, /, loadout_index: int, character_id: int, membership_type: Union[int, MembershipType]) -> None:
2283    async def clear_loadout(
2284        self,
2285        access_token: str,
2286        /,
2287        loadout_index: int,
2288        character_id: int,
2289        membership_type: typedefs.IntAnd[enums.MembershipType],
2290    ) -> None:
2291        response = await self._request(
2292            RequestMethod.POST,
2293            "Destiny2/Actions/Loadouts/ClearLoadout/",
2294            json={
2295                "loadoutIndex": loadout_index,
2296                "characterId": character_id,
2297                "membership_type": int(membership_type),
2298            },
2299            auth=access_token,
2300        )
2301        assert isinstance(response, int)

Clear the identifiers and items of a loadout.

This operation requires MoveEquipDestinyItems OAuth2 scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • loadout_index (int): The index of the loadout to use.
  • character_id (int): The character ID to equip the loadout to.
  • membership_type (aiobungie.MembershipType | int): The membership type of the account.
class RESTPool:
195class RESTPool:
196    """Pool of `RESTClient` instances.
197
198    This allows to create multiple instances of `RESTClient`s that can be acquired
199    which share the same TCP connector.
200
201    Example
202    -------
203    ```py
204    import aiobungie
205
206    client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')
207
208    # Using a context manager to acquire an instance
209    # from the pool and close it after.
210    async with client_pool.acquire() as rest:
211        ...
212    ```
213
214    Parameters
215    ----------
216    token : `str`
217        A valid application token from Bungie's developer portal.
218
219    Other Parameters
220    ----------------
221    max_retries : `int`
222        The max retries number to retry if the request hit a `5xx` status code.
223    client_secret : `typing.Optional[str]`
224        An optional application client secret,
225        This is only needed if you're fetching OAuth2 tokens with this client.
226    client_id : `typing.Optional[int]`
227        An optional application client id,
228        This is only needed if you're fetching OAuth2 tokens with this client.
229    enable_debugging : `bool | str`
230        Whether to enable logging responses or not.
231
232    Logging Levels
233    --------------
234    * `False`: This will disable logging.
235    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
236    Like the response status, route, taken time and so on.
237    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
238    """
239
240    __slots__ = (
241        "_token",
242        "_max_retries",
243        "_client_secret",
244        "_client_id",
245        "_metadata",
246        "_enable_debug",
247        "_client_session",
248        "_loads",
249        "_dumps",
250    )
251
252    # Looks like mypy doesn't like this.
253    if typing.TYPE_CHECKING:
254        _enable_debug: typing.Union[typing.Literal["TRACE"], bool, int]
255
256    def __init__(
257        self,
258        token: str,
259        /,
260        *,
261        client_secret: typing.Optional[str] = None,
262        client_id: typing.Optional[int] = None,
263        client_session: typing.Optional[aiohttp.ClientSession] = None,
264        dumps: typedefs.Dumps = helpers.dumps,
265        loads: typedefs.Loads = helpers.loads,
266        max_retries: int = 4,
267        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
268    ) -> None:
269        self._client_secret = client_secret
270        self._client_id = client_id
271        self._token = token
272        self._max_retries = max_retries
273        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
274        self._enable_debug = enable_debugging
275        self._client_session = client_session
276        self._loads = loads
277        self._dumps = dumps
278
279    @property
280    def client_id(self) -> typing.Optional[int]:
281        return self._client_id
282
283    @property
284    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
285        """Pool's Metadata. This is different from client instance metadata."""
286        return self._metadata
287
288    @typing.final
289    def acquire(self) -> RESTClient:
290        """Acquires a new `RESTClient` instance from this REST pool.
291
292        Returns
293        -------
294        `RESTClient`
295            An instance of a REST client.
296        """
297        return RESTClient(
298            self._token,
299            client_secret=self._client_secret,
300            client_id=self._client_id,
301            loads=self._loads,
302            dumps=self._dumps,
303            max_retries=self._max_retries,
304            enable_debugging=self._enable_debug,
305            client_session=self._client_session,
306        )

Pool of RESTClient instances.

This allows to create multiple instances of RESTClients that can be acquired which share the same TCP connector.

Example
import aiobungie

client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')

# Using a context manager to acquire an instance
# from the pool and close it after.
async with client_pool.acquire() as rest:
    ...
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information. Like the response status, route, taken time and so on.
  • "TRACE" | TRACE: This will log the response headers along with the minimal information.
RESTPool( token: str, /, *, client_secret: Optional[str] = None, client_id: Optional[int] = None, client_session: Optional[aiohttp.client.ClientSession] = None, dumps: collections.abc.Callable[[dict[str, typing.Any] | list[typing.Any]], bytes] = <function dumps>, loads: collections.abc.Callable[[str | bytes], list[typing.Any] | dict[str, typing.Any]] = <function loads>, max_retries: int = 4, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
256    def __init__(
257        self,
258        token: str,
259        /,
260        *,
261        client_secret: typing.Optional[str] = None,
262        client_id: typing.Optional[int] = None,
263        client_session: typing.Optional[aiohttp.ClientSession] = None,
264        dumps: typedefs.Dumps = helpers.dumps,
265        loads: typedefs.Loads = helpers.loads,
266        max_retries: int = 4,
267        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
268    ) -> None:
269        self._client_secret = client_secret
270        self._client_id = client_id
271        self._token = token
272        self._max_retries = max_retries
273        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
274        self._enable_debug = enable_debugging
275        self._client_session = client_session
276        self._loads = loads
277        self._dumps = dumps
client_id: Optional[int]
metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

Pool's Metadata. This is different from client instance metadata.

@typing.final
def acquire(self) -> RESTClient:
288    @typing.final
289    def acquire(self) -> RESTClient:
290        """Acquires a new `RESTClient` instance from this REST pool.
291
292        Returns
293        -------
294        `RESTClient`
295            An instance of a REST client.
296        """
297        return RESTClient(
298            self._token,
299            client_secret=self._client_secret,
300            client_id=self._client_id,
301            loads=self._loads,
302            dumps=self._dumps,
303            max_retries=self._max_retries,
304            enable_debugging=self._enable_debug,
305            client_session=self._client_session,
306        )

Acquires a new RESTClient instance from this REST pool.

Returns
@typing.final
class Race(builtins.int, aiobungie.Enum):
485@typing.final
486class Race(int, Enum):
487    """An Enum for Destiny races."""
488
489    HUMAN = 0
490    AWOKEN = 1
491    EXO = 2
492    UNKNOWN = 3

An Enum for Destiny races.

HUMAN = <Race.HUMAN: 0>
AWOKEN = <Race.AWOKEN: 1>
EXO = <Race.EXO: 2>
UNKNOWN = <Race.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Raid(builtins.int, aiobungie.Enum):
133@typing.final
134class Raid(int, Enum):
135    """An Enum for all available raids in Destiny 2."""
136
137    DSC = 910380154
138    """Deep Stone Crypt"""
139
140    LW = 2122313384
141    """Last Wish"""
142
143    VOG = 3881495763
144    """Normal Valut of Glass"""
145
146    GOS = 3458480158
147    """Garden Of Salvation"""

An Enum for all available raids in Destiny 2.

DSC = <Raid.DSC: 910380154>

Deep Stone Crypt

LW = <Raid.LW: 2122313384>

Last Wish

VOG = <Raid.VOG: 3881495763>

Normal Valut of Glass

GOS = <Raid.GOS: 3458480158>

Garden Of Salvation

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class RateLimitedError(aiobungie.HTTPError):
252@attrs.define(auto_exc=True)
253class RateLimitedError(HTTPError):
254    """Raised when too many request status code is returned."""
255
256    http_status: http.HTTPStatus = attrs.field(
257        default=http.HTTPStatus.TOO_MANY_REQUESTS, init=False
258    )
259    """The request response http status."""
260
261    url: typedefs.StrOrURL
262    """The URL/endpoint caused this error."""
263
264    body: typing.Any
265    """The response body."""
266
267    retry_after: float = attrs.field(default=0.0)
268    """The amount of seconds you need to wait before retrying to requests."""
269
270    message: str = attrs.field(init=False)
271    """A Bungie human readable message describes the cause of the error."""
272
273    @message.default  # type: ignore
274    def _(self) -> str:
275        return f"You're ratelimited for {self.retry_after}, Endpoint: {self.url}. Slow down!"
276
277    def __str__(self) -> str:
278        return self.message

Raised when too many request status code is returned.

RateLimitedError(url: Union[str, yarl.URL], body: Any, retry_after: float = 0.0)
2def __init__(self, url, body, retry_after=attr_dict['retry_after'].default):
3    self.http_status = attr_dict['http_status'].default
4    self.url = url
5    self.body = body
6    self.retry_after = retry_after
7    self.message = __attr_factory_message(self)
8    BaseException.__init__(self, self.url,self.body,self.retry_after)

Method generated by attrs for class RateLimitedError.

http_status: http.HTTPStatus

The request response http status.

url: Union[str, yarl.URL]

The URL/endpoint caused this error.

body: Any

The response body.

retry_after: float

The amount of seconds you need to wait before retrying to requests.

message: str

A Bungie human readable message describes the cause of the error.

Inherited Members
builtins.BaseException
with_traceback
args
@typing.final
class RecordState(aiobungie.Flag):
48@typing.final
49class RecordState(enums.Flag):
50    """An enum for records component states."""
51
52    NONE = 0
53    REDEEMED = 1 << 0
54    UNAVAILABLE = 1 << 1
55    OBJECTIVE_NOT_COMPLETED = 1 << 2
56    OBSCURED = 1 << 3
57    INVISIBLE = 1 << 4
58    ENTITLEMENT_UNOWNED = 1 << 5
59    CAN_EQUIP_TITLE = 1 << 6

An enum for records component states.

NONE = <RecordState.NONE: 0>
REDEEMED = <RecordState.REDEEMED: 1>
UNAVAILABLE = <RecordState.UNAVAILABLE: 2>
OBJECTIVE_NOT_COMPLETED = <RecordState.OBJECTIVE_NOT_COMPLETED: 4>
OBSCURED = <RecordState.OBSCURED: 8>
INVISIBLE = <RecordState.INVISIBLE: 16>
ENTITLEMENT_UNOWNED = <RecordState.ENTITLEMENT_UNOWNED: 32>
CAN_EQUIP_TITLE = <RecordState.CAN_EQUIP_TITLE: 64>
Inherited Members
Flag
name
value
@typing.final
class Relationship(builtins.int, aiobungie.Enum):
680@typing.final
681class Relationship(int, Enum):
682    """An enum for bungie friends relationship types."""
683
684    UNKNOWN = 0
685    FRIEND = 1
686    INCOMING_REQUEST = 2
687    OUTGOING_REQUEST = 3

An enum for bungie friends relationship types.

UNKNOWN = <Relationship.UNKNOWN: 0>
FRIEND = <Relationship.FRIEND: 1>
INCOMING_REQUEST = <Relationship.INCOMING_REQUEST: 2>
OUTGOING_REQUEST = <Relationship.OUTGOING_REQUEST: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RequestMethod(builtins.str, aiobungie.Enum):
180class RequestMethod(str, enums.Enum):
181    """HTTP request methods enum."""
182
183    GET = "GET"
184    """GET methods."""
185    POST = "POST"
186    """POST methods."""
187    PUT = "PUT"
188    """PUT methods."""
189    PATCH = "PATCH"
190    """PATCH methods."""
191    DELETE = "DELETE"
192    """DELETE methods"""

HTTP request methods enum.

GET = <RequestMethod.GET: GET>

GET methods.

POST = <RequestMethod.POST: POST>

POST methods.

PUT = <RequestMethod.PUT: PUT>

PUT methods.

PATCH = <RequestMethod.PATCH: PATCH>

PATCH methods.

DELETE = <RequestMethod.DELETE: DELETE>

DELETE methods

Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@attrs.define(auto_exc=True)
class ResponseError(aiobungie.HTTPException):
247@attrs.define(auto_exc=True)
248class ResponseError(HTTPException):
249    """Exception for other HTTP response errors."""

Exception for other HTTP response errors.

ResponseError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class ResponseError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
@typing.final
class Stat(builtins.int, aiobungie.Enum):
507@typing.final
508class Stat(int, Enum):
509    """An Enum for Destiny 2 character stats."""
510
511    NONE = 0
512    MOBILITY = 2996146975
513    RESILIENCE = 392767087
514    RECOVERY = 1943323491
515    DISCIPLINE = 1735777505
516    INTELLECT = 144602215
517    STRENGTH = 4244567218
518    LIGHT_POWER = 1935470627

An Enum for Destiny 2 character stats.

NONE = <Stat.NONE: 0>
MOBILITY = <Stat.MOBILITY: 2996146975>
RESILIENCE = <Stat.RESILIENCE: 392767087>
RECOVERY = <Stat.RECOVERY: 1943323491>
DISCIPLINE = <Stat.DISCIPLINE: 1735777505>
INTELLECT = <Stat.INTELLECT: 144602215>
STRENGTH = <Stat.STRENGTH: 4244567218>
LIGHT_POWER = <Stat.LIGHT_POWER: 1935470627>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
TRACE = 5
@typing.final
class TierType(builtins.int, aiobungie.Enum):
622@typing.final
623class TierType(int, Enum):
624    """An enum for a Destiny 2 item tier type."""
625
626    UNKNOWN = 0
627    CURRENCY = 1
628    BASIC = 2
629    COMMON = 3
630    RARE = 4
631    SUPERIOR = 5
632    EXOTIC = 6

An enum for a Destiny 2 item tier type.

UNKNOWN = <TierType.UNKNOWN: 0>
CURRENCY = <TierType.CURRENCY: 1>
BASIC = <TierType.BASIC: 2>
COMMON = <TierType.COMMON: 3>
RARE = <TierType.RARE: 4>
SUPERIOR = <TierType.SUPERIOR: 5>
EXOTIC = <TierType.EXOTIC: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class TransferStatus(aiobungie.Flag):
732@typing.final
733class TransferStatus(Flag):
734    """An enum for items transfer statuses."""
735
736    CAN_TRANSFER = 0
737    """The item can be transferred."""
738    IS_EQUIPPED = 1 << 0
739    """You can't transfer since the item is equipped."""
740    NOT_TRASNFERRABLE = 1 << 1
741    """This item can not be transferred."""
742    COULD_BE_TRANSFERRED = 1 << 2
743    """You can transfer the item. But the place you're trying to put it at has no space for it."""

An enum for items transfer statuses.

CAN_TRANSFER = <TransferStatus.CAN_TRANSFER: 0>

The item can be transferred.

IS_EQUIPPED = <TransferStatus.IS_EQUIPPED: 1>

You can't transfer since the item is equipped.

NOT_TRASNFERRABLE = <TransferStatus.NOT_TRASNFERRABLE: 2>

This item can not be transferred.

COULD_BE_TRANSFERRED = <TransferStatus.COULD_BE_TRANSFERRED: 4>

You can transfer the item. But the place you're trying to put it at has no space for it.

Inherited Members
Flag
name
value
UNDEFINED = UNDEFINED
@attrs.define(auto_exc=True)
class Unauthorized(aiobungie.HTTPException):
154@attrs.define(auto_exc=True)
155class Unauthorized(HTTPException):
156    """An exception that's raised when trying to make unauthorized call to a resource and it returns 404."""
157
158    http_status: http.HTTPStatus = attrs.field(
159        default=http.HTTPStatus.UNAUTHORIZED, init=False
160    )

An exception that's raised when trying to make unauthorized call to a resource and it returns 404.

Unauthorized( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Unauthorized.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
args
UndefinedOr = typing.Union[UndefinedType, +_T]
class UndefinedType:
33class UndefinedType:
34    """An `UNDEFINED` type."""
35
36    __instance: typing.Optional[UndefinedType] = None
37
38    def __bool__(self) -> typing.Literal[False]:
39        return False
40
41    def __int__(self) -> typing.Literal[0]:
42        return 0
43
44    def __repr__(self) -> str:
45        return "UNDEFINED"
46
47    def __str__(self) -> str:
48        return "UNDEFINED"
49
50    def __new__(cls) -> UndefinedType:
51        if cls.__instance is None:
52            o = super().__new__(cls)
53            cls.__instance = o
54        return cls.__instance

An UNDEFINED type.

@typing.final
class ValueUIStyle(builtins.int, aiobungie.Enum):
75@typing.final
76class ValueUIStyle(int, enums.Enum):
77    AUTOMATIC = 0
78    FRACTION = 1
79    CHECK_BOX = 2
80    PERCENTAGE = 3
81    DATETIME = 4
82    FRACTION_FLOAT = 5
83    INTEGER = 6
84    TIME_DURATION = 7
85    HIDDEN = 8
86    MULTIPLIER = 9
87    GREEN_PIPS = 10
88    RED_PIPS = 11
89    EXPLICIT_PERCENTAGE = 12
90    RAW_FLOAT = 13
91    LEVEL_AND_REWARD = 14

An enumeration.

AUTOMATIC = <ValueUIStyle.AUTOMATIC: 0>
FRACTION = <ValueUIStyle.FRACTION: 1>
CHECK_BOX = <ValueUIStyle.CHECK_BOX: 2>
PERCENTAGE = <ValueUIStyle.PERCENTAGE: 3>
DATETIME = <ValueUIStyle.DATETIME: 4>
FRACTION_FLOAT = <ValueUIStyle.FRACTION_FLOAT: 5>
INTEGER = <ValueUIStyle.INTEGER: 6>
TIME_DURATION = <ValueUIStyle.TIME_DURATION: 7>
HIDDEN = <ValueUIStyle.HIDDEN: 8>
MULTIPLIER = <ValueUIStyle.MULTIPLIER: 9>
GREEN_PIPS = <ValueUIStyle.GREEN_PIPS: 10>
RED_PIPS = <ValueUIStyle.RED_PIPS: 11>
EXPLICIT_PERCENTAGE = <ValueUIStyle.EXPLICIT_PERCENTAGE: 12>
RAW_FLOAT = <ValueUIStyle.RAW_FLOAT: 13>
LEVEL_AND_REWARD = <ValueUIStyle.LEVEL_AND_REWARD: 14>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Vendor(builtins.int, aiobungie.Enum):
230@typing.final
231class Vendor(int, Enum):
232    """An Enum for all available vendors in Destiny 2."""
233
234    ZAVALA = 69482069
235    XUR = 2190858386
236    BANSHE = 672118013
237    SPIDER = 863940356
238    SHAXX = 3603221665
239    KADI = 529635856
240    """Postmaster exo."""
241    YUNA = 1796504621
242    """Asia servers only."""
243    EVERVERSE = 3361454721
244    AMANDA = 460529231
245    """Amanda holiday"""
246    CROW = 3611983588
247    HAWTHORNE = 3347378076
248    ADA1 = 350061650
249    DRIFTER = 248695599
250    IKORA = 1976548992
251    SAINT = 765357505
252    """Saint-14"""
253    ERIS_MORN = 1616085565
254    SHAW_HAWN = 1816541247
255    """COSMODROME Guy"""
256    VARIKS = 2531198101

An Enum for all available vendors in Destiny 2.

ZAVALA = <Vendor.ZAVALA: 69482069>
XUR = <Vendor.XUR: 2190858386>
BANSHE = <Vendor.BANSHE: 672118013>
SPIDER = <Vendor.SPIDER: 863940356>
SHAXX = <Vendor.SHAXX: 3603221665>
KADI = <Vendor.KADI: 529635856>

Postmaster exo.

YUNA = <Vendor.YUNA: 1796504621>

Asia servers only.

EVERVERSE = <Vendor.EVERVERSE: 3361454721>
AMANDA = <Vendor.AMANDA: 460529231>

Amanda holiday

CROW = <Vendor.CROW: 3611983588>
HAWTHORNE = <Vendor.HAWTHORNE: 3347378076>
ADA1 = <Vendor.ADA1: 350061650>
DRIFTER = <Vendor.DRIFTER: 248695599>
IKORA = <Vendor.IKORA: 1976548992>
SAINT = <Vendor.SAINT: 765357505>

Saint-14

ERIS_MORN = <Vendor.ERIS_MORN: 1616085565>
SHAW_HAWN = <Vendor.SHAW_HAWN: 1816541247>

COSMODROME Guy

VARIKS = <Vendor.VARIKS: 2531198101>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class WeaponType(builtins.int, aiobungie.Enum):
521@typing.final
522class WeaponType(int, Enum):
523    """Enums for The three Destiny Weapon Types"""
524
525    NONE = 0
526    KINETIC = 1498876634
527    ENERGY = 2465295065
528    POWER = 953998645

Enums for The three Destiny Weapon Types

NONE = <WeaponType.NONE: 0>
KINETIC = <WeaponType.KINETIC: 1498876634>
ENERGY = <WeaponType.ENERGY: 2465295065>
POWER = <WeaponType.POWER: 953998645>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
annotations = _Feature((3, 7, 0, 'beta', 1), (3, 11, 0, 'alpha', 0), 16777216)
def iter( iterable: collections.abc.Iterable[~Item]) -> Iterator[~Item]:
574def iter(
575    iterable: collections.Iterable[Item],
576) -> Iterator[Item]:
577    """Transform an iterable into an flat iterator.
578
579    Example
580    -------
581    ```py
582    sequence = [1,2,3]
583    for item in aiobungie.iter(sequence).reversed():
584        print(item)
585    # 3
586    # 2
587    # 1
588    ```
589
590    Parameters
591    ----------
592    iterable: `typing.Iterable[Item]`
593        The iterable to convert.
594
595    Raises
596    ------
597    `StopIteration`
598        If no elements are left in the iterator.
599    """
600    return Iterator(iterable)

Transform an iterable into an flat iterator.

Example
sequence = [1,2,3]
for item in aiobungie.iter(sequence).reversed():
    print(item)
# 3
# 2
# 1
Parameters
  • iterable (typing.Iterable[Item]): The iterable to convert.
Raises
  • StopIteration: If no elements are left in the iterator.
async def raise_error( response: aiohttp.client_reqrep.ClientResponse) -> AiobungieError:
281async def raise_error(response: aiohttp.ClientResponse) -> AiobungieError:
282    """Generates and raise exceptions on error responses."""
283
284    # Not a JSON response, raise immediately.
285
286    # Also Bungie sometimes get funky and return HTML instead of JSON when making an authorized
287    # request with a dummy access token. I can't really do anything about this..
288    if response.content_type != "application/json":
289        return HTTPError(
290            f"Expected JSON content but got {response.content_type!s}, {response.real_url!s}",
291            http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE,
292        )
293
294    body = await response.json()
295    message: str = body.get("Message", "UNDEFINED_MESSAGE")
296    error_status: str = body.get("ErrorStatus", "UNDEFINED_ERROR_STATUS")
297    message_data: dict[str, str] = body.get("MessageData", {})
298    throttle_seconds: int = body.get("ThrottleSeconds", 0)
299    error_code: int = body.get("ErrorCode", 0)
300
301    # Standard HTTP status.
302    if response.status == http.HTTPStatus.NOT_FOUND:
303        return NotFound(
304            message=message,
305            error_code=error_code,
306            throttle_seconds=throttle_seconds,
307            url=str(response.real_url),
308            body=body,
309            headers=response.headers,
310            error_status=error_status,
311            message_data=message_data,
312        )
313
314    elif response.status == http.HTTPStatus.FORBIDDEN:
315        return Forbidden(
316            message=message,
317            error_code=error_code,
318            throttle_seconds=throttle_seconds,
319            url=str(response.real_url),
320            body=body,
321            headers=response.headers,
322            error_status=error_status,
323            message_data=message_data,
324        )
325
326    elif response.status == http.HTTPStatus.UNAUTHORIZED:
327        return Unauthorized(
328            message=message,
329            error_code=error_code,
330            throttle_seconds=throttle_seconds,
331            url=str(response.real_url),
332            body=body,
333            headers=response.headers,
334            error_status=error_status,
335            message_data=message_data,
336        )
337
338    elif response.status == http.HTTPStatus.BAD_REQUEST:
339        # Membership needs to be alone.
340        if error_status == "InvalidParameters":
341            return MembershipTypeError(
342                message=message,
343                body=body,
344                headers=response.headers,
345                url=str(response.url),
346                membership_type=message_data["membershipType"],
347                required_membership=message_data["membershipInfo.membershipType"],
348                membership_id=int(message_data["membershipId"]),
349            )
350        return BadRequest(
351            message=message,
352            body=body,
353            headers=response.headers,
354            url=str(response.url),
355        )
356
357    status = http.HTTPStatus(response.status)
358
359    if 400 <= status < 500:
360        return ResponseError(
361            message=message,
362            error_code=error_code,
363            throttle_seconds=throttle_seconds,
364            url=str(response.real_url),
365            body=body,
366            headers=response.headers,
367            error_status=error_status,
368            message_data=message_data,
369            http_status=status,
370        )
371
372    # Need to self handle ~5xx errors
373    elif 500 <= status < 600:
374        # No API key or method requires OAuth2 most likely.
375        if error_status in {
376            "ApiKeyMissingFromRequest",
377            "WebAuthRequired",
378            "ApiInvalidOrExpiredKey",
379            "AuthenticationInvalid",
380            "AuthorizationCodeInvalid",
381        }:
382            return Unauthorized(
383                message=message,
384                error_code=error_code,
385                throttle_seconds=throttle_seconds,
386                url=str(response.real_url),
387                body=body,
388                headers=response.headers,
389                error_status=error_status,
390                message_data=message_data,
391            )
392
393        # Anything contains not found.
394        elif (
395            "NotFound" in error_status or error_status == "UserCannotFindRequestedUser"
396        ):
397            return NotFound(
398                message=message,
399                error_code=error_code,
400                throttle_seconds=throttle_seconds,
401                url=str(response.real_url),
402                body=body,
403                headers=response.headers,
404                error_status=error_status,
405                message_data=message_data,
406            )
407
408        # Other 5xx errors.
409        else:
410            return InternalServerError(
411                message=message,
412                error_code=error_code,
413                throttle_seconds=throttle_seconds,
414                url=str(response.real_url),
415                body=body,
416                headers=response.headers,
417                error_status=error_status,
418                message_data=message_data,
419                http_status=status,
420            )
421    # Something else.
422    else:
423        return HTTPException(
424            message=message,
425            error_code=error_code,
426            throttle_seconds=throttle_seconds,
427            url=str(response.real_url),
428            body=body,
429            headers=response.headers,
430            error_status=error_status,
431            message_data=message_data,
432            http_status=status,
433        )

Generates and raise exceptions on error responses.

def stringify_http_message(headers: collections.abc.Mapping[str, str]) -> str:
436def stringify_http_message(headers: collections.Mapping[str, str]) -> str:
437    return (
438        "{ \n"
439        + "\n".join(  # noqa: W503
440            f"{f'   {key}'}: {value}"
441            if key not in ("Authorization", "X-API-KEY")
442            else f"   {key}: HIDDEN_TOKEN"
443            for key, value in headers.items()
444        )
445        + "\n}"  # noqa: W503
446    )